Mysql
 sql >> база данни >  >> RDS >> Mysql

Django views.py Версия на SQL присъединяване със заявка за няколко таблици

Е, това са някои неясни имена на таблици и полета, но най-добре мога да кажа, че заявката ще изглежда нещо като:

(Restaurant.objects.filter(city=8, 
     cuisine__cuisinetype__cuisine="Italian").distinct().order_by('name')[:20])

Но освен ако не сте заключени в тази схема на база данни, вашите модели ще изглеждат по-добре като:

class CuisineType(models.Model):
    name = models.CharField(max_length=50)
    class Meta:
        db_table = 'cuisinetype'

class Restaurants(models.Model):
    city = models.ForeignKey("City", null=True, blank=True) # Apparently defined elsewhere. Should be part of location?
    name = models.CharField(max_length=50)
    location = models.ForeignKey("Location", null=True, blank=True) # Apparently defined elsewhere.
    cuisines = models.ManyToManyField(CuisineType)

Тогава заявката би била по-скоро:

Restaurant.objects.filter(city=8, cuisines__name="Italian").order_by('name')[:20]

Добре, нека преминем през вашата заявка, като приемем, че няма промени в кода ви. Ще започнем с подзаявката.

SELECT DISTINCT res_id FROM cuisine 
        JOIN    cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
        WHERE   cuisinetype.`cuisine` = 'Italian'

Разглеждаме клаузата WHERE и виждаме, че имаме нужда от JOIN. За да направите присъединяване, трябва да декларирате релационно поле в един от присъединените модели (Django ще добави обратна връзка, която трябва да назовем). Така че съпоставяме cuisine.cuisineid с `cuisinetype.cuisineid. Това е ужасно именуване.

Това е релация много към много, така че имаме нужда от ManyToManyField . Е, гледайки Cuisine модел, това наистина е свързващата маса за този M2M. Django очаква присъединяваща се таблица да има два ForeignKey полета, по едно насочено към всяка страна на ставата. Обикновено това ще създаде това за вас, за да запазите здравия разум. Явно не си толкова късметлия. Така че трябва да го свържете ръчно.

Изглежда, че полето "GID" е (безполезно) поле за идентификация за записа, така че нека приемем, че е цяло число с автоматично увеличение. (За да сте сигурни, проверете командите CREATE TABLE.) Сега можем да пренапишем Cuisine модел в нещо, което се доближава до разумно:

class Cuisine(models.Model):
    cuisinegid = models.AutoField(primary_key=True, db_column='CuisineGID')
    cuisineid = models.ForeignKey("Cuisinetype", null=True, 
        db_column='CuisineID', blank=True)
    res_id = models.ForeignKey("Restaurant", null=True, db_column='Res_ID', 
        blank=True)
    class Meta:
        db_table = 'cuisine'

Имената на моделите са цитирани, защото моделите все още не са дефинирани (те са по-късно във файла). Сега няма изискване имената на полетата в Django да съвпадат с имената на колоните, така че нека ги променим на нещо по-четливо. Полето за идентификатор на запис обикновено се нарича просто id , а външните ключове обикновено се наименуват според това, с което се отнасят:

class Cuisine(models.Model):
    id = models.AutoField(primary_key=True, db_column='CuisineGID')
    cuisine_type = models.ForeignKey("CuisineType", null=True, 
        db_column='CuisineID', blank=True)
    restaurant = models.ForeignKey("Restaurant", null=True, db_column='Res_ID', 
        blank=True)
    class Meta:
        db_table = 'cuisine'

Добре, приключихме с дефинирането на общата ни маса. Докато сме на това, нека приложим същите неща към нашия Cuisinetype модел. Обърнете внимание на коригираното име на клас с камила:

class CuisineType(models.Model):
    id = models.AutoField(primary_key=True, db_column='CuisineID')
    name = models.CharField(max_length=50, db_column='Cuisine', blank=True)
    class Meta:
        db_table = 'cuisinetype'

И така най-накрая стигаме до нашия Restaurant модел. Имайте предвид, че името е единствено число; обект представлява само един запис.

Забелязвам, че липсва всякаква dp_table или db_column неща, така че излизам на крайник и предполагам, че Django го създава. Това означава, че можем да му позволим да създаде id поле за нас и можем да го пропуснем от нашия код. (Ако случаят не е такъв, просто го добавяме както при другите модели. Но наистина не трябва да имате нулев идентификационен номер на запис.) И това е мястото, където нашата кухня тип ManyToManyField животи:

class Restaurants(models.Model):
    city_id = models.ForeignKey(null=True, blank=True)
    name = models.CharField(max_length=50, blank=True)
    location = models.ForeignKey(null=True, blank=True)
    cuisine_types = models.ManyToManyField(CuisineType, through=Cuisine,
        null=True, blank=True)

Имайте предвид, че името на полето M2M е множествено число, тъй като тази връзка води до множество записи.

Още нещо, което ще искаме да добавим към този модел, са имената на обратните връзки. С други думи, как да се върнем от другите модели обратно към Restaurant . Правим това, като добавяме related_name параметри. Не е необичайно да са еднакви.

class Restaurant(models.Model):
    city_id = models.ForeignKey(null=True, blank=True, 
        related_name="restaurants")
    name = models.CharField(max_length=50, blank=True)
    location = models.ForeignKey(null=True, blank=True, 
        related_name="restaurants")
    cuisine_types = models.ManyToManyField(CuisineType, through=Cuisine,
        null=True, blank=True, related_name="restaurants")

Сега най-накрая сме готови. Така че нека разгледаме вашата заявка:

SELECT  restaurants.`name`, restaurants.`address`, cuisinetype.`cuisine`
FROM    restaurants
JOIN    cuisinetype ON cuisinetype.cuisineid = restaurants.`cuisine`
WHERE   city_id = 8 AND restaurants.id IN (
        SELECT DISTINCT res_id FROM cuisine 
        JOIN    cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
        WHERE   cuisinetype.`cuisine` = 'Italian')
ORDER BY restaurants.`name`
LIMIT 20

Тъй като това е FROM restaurants , ще започнем с обектния мениджър по подразбиране на този модел, objects :

Restaurant.objects

WHERE клаузата в този случай е filter() обаждане, така че го добавяме за първия термин:

Restaurant.objects.filter(city=8)

Можете да изтриете стойност на първичен ключ или City обект от дясната страна на този термин. Останалата част от заявката става по-сложна, защото се нуждае от JOIN . Присъединяването в Django просто изглежда като дерефериране през полето за релация. В заявка това означава свързване на съответните имена на полета с двойно долно подчертаване:

Restaurant.objects.filter(city=8, cuisine_type__name="Italian")

Django знае към кои полета да се присъедини, защото това е декларирано в Cuisine таблица, която се изтегля от through=Cuisine параметър в cuisine_types . също така знае да направи подзаявка, защото преминавате през M2M релация.

Така че това ни прави SQL еквивалентен на:

SELECT  restaurants.`name`, restaurants.`address`
FROM    restaurants
WHERE   city_id = 8 AND restaurants.id IN (
        SELECT res_id FROM cuisine 
        JOIN    cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
        WHERE   cuisinetype.`cuisine` = 'Italian')

На половината път. Сега ни трябва SELECT DISTINCT така че не получаваме множество копия на един и същ запис:

Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()

И трябва да изтеглите видовете кухня за показване. Оказва се, че заявката, която имате, е неефективна там, защото ви отвежда само до таблицата за присъединяване и трябва да изпълнявате допълнителни заявки, за да получите свързания CuisineType записи. Познайте какво:Django ви е покрил.

(Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
    .prefetch_related("cuisine_types"))

Django ще изпълни две заявки:една като вашата, за да получи съвместните идентификатори и още една, за да получи свързания CuisineType записи. След това достъпът чрез резултата от заявката не е необходимо да се връща към базата данни.

Последните две неща са подреждането:

(Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
    .prefetch_related("cuisine_types").order_by("name"))

И LIMIT :

(Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
    .prefetch_related("cuisine_types").order_by("name")[:20])

И ето вашата заявка (и свързаната с нея заявка), опакована в два реда на Python. Имайте предвид, че към този момент заявката дори не е изпълнена. Трябва да го поставите в нещо, като шаблон, преди да направи нещо:

def cuisinesearch(request, cuisine):
    return render_to_response('cuisinesearch.html', {
        'restaurants': (Restaurant.objects.filter(city=8, 
             cuisine_type__name="Italian").distinct()
             .prefetch_related("cuisine_types").order_by("name")[:20])
        })

Шаблон:

{% for restaurant in cuisinesearch %}
<h2>{{ restaurant.name }}</h2>
<div class="location">{{ restaurant.location }}</div>
<h3>Cuisines:</h3>
<ul class="cuisines">{% for ct in restaurant.cuisine_types.all %}
<li>{{ ct.name }}</li>{% endfor %}
</ul>
{% endfor %}



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да актуализирам таблица автоматично, когато друга таблица се актуализира на различен mysql сървър?

  2. PHP Сортиране на най-близките координати

  3. MySQL - CONCAT - Има ли начин да се свърже низ и да се използва като променлива?

  4. Как да добавя нов език за съобщенията за грешки на mysql?

  5. Изчислете процентил от честотата в MySQL