Django views.py. Версия SQL. Присоединение к Multi Table Query

Нужна помощь в Django-версии SQL-запроса с несколькими таблицами. Запрос использует 3 таблицы для получения названия ресторана, адрес из Restaurants table и типа кухни из Cuisinetypes table . Все на основе названия кухни, которое передается по URL-адресу, и идентификатор кухни хранится в таблице кухни.

Models.py

 class Restaurant(models.Model): name = models.CharField(max_length=50, db_column='name', blank=True) slugname = models.SlugField(max_length=50, blank=True) address = models.CharField(max_length=100, blank=True) city = models.ForeignKey('City', related_name="restaurants") location = models.ForeignKey('Location', related_name="restaurants") hood = models.ForeignKey('Hood', null=True, blank=True, related_name="restaurants") listingrole = models.ForeignKey('Listingrole', related_name="restaurants") cuisine_types = models.ManyToManyField('Cuisinetype', null=True, blank=True, related_name="restaurants") class Meta: db_table = 'restaurant' class City(models.Model): name = models.CharField(max_length=50, db_column='city') state = models.CharField(max_length=50, blank=True, null=True) switch = models.SmallIntegerField(null=True, blank=True, default='1') class Meta: db_table = 'city' class Cuisinetype(models.Model): name = models.CharField(max_length=50, db_column='cuisine', blank=True) # Field name made lowercase. switch = models.SmallIntegerField(null=True, blank=True, default='1') class Meta: db_table = 'cuisinetype' class Location(models.Model): name = models.CharField(max_length=50, db_column='location', blank=False, null=False) city = models.ForeignKey('City', related_name="locations") switch = models.SmallIntegerField(null=True, blank=True, default='1') class Meta: db_table = 'location' class Hood(models.Model): name = models.CharField(max_length=50, db_column='hood') city = models.ForeignKey('City', related_name='hoods') location = models.ForeignKey('Location', related_name='hoods') switch = models.SmallIntegerField(null=True, blank=True, default='1') class Meta: db_table = 'hood' class Listingrole(models.Model): id = models.AutoField(primary_key=True, db_column='id') name = models.CharField(max_length=50, db_column='listingrole', blank=True) # Field name made lowercase. switch = models.SmallIntegerField(null=True, blank=True, default='1') class Meta: db_table = 'listingrole' .... 

urls.py

 url(r'^cuisine/(?P<cuisine>[-\w]+)/$', 'views.cuisinesearch'), 

views.py

 def cuisinesearch(request, name='unknown'): name = name.replace('-', ' ').capitalize() return render_to_response('cuisinesearch.html', {'cuisinesearch': Restaurant.objects.filter(city_id=8, switch=1, listingrole__in=[1,2,3,4], cuisine_types__name=name) .distinct().prefetch_related("cuisine_types").order_by('listingrole', 'displayorder')[:50] }) 

HTML

И каков будет правильный способ отображения запроса?

 {% 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 %} 

  • Sqlalchemy странное поведение при вставке нескольких строк и возврате первичного ключа
  • Каковы реальные уровни абстракции базы данных для Python
  • Как увидеть реальный SQL-запрос в Python cursor.execute
  • Python, конвертировать 9 кортежей UTC в формат MySQL datetime
  • Pandas read_sql с параметрами
  • pyparsing, Every, название результатов
  • Возврат вывода скрипта Python CGI MySQL
  • Используя pandas.io.sql.read_frame, могу ли я parse_dates, как в read_csv?
  • One Solution collect form web for “Django views.py. Версия SQL. Присоединение к Multi Table Query”

    Ну, это некоторые нечеткие имена таблиц и полей, но лучше всего я могу сказать, что запрос будет выглядеть примерно так:

     (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 добавит обратное отношение, которое мы должны назвать). Таким образом, мы сопоставляем кухню. Кулинария с `cuisinetype.cuisineid. Это ужасное название.

    Это отношение «много-ко-многим», поэтому нам нужен ManyToManyField . Ну, глядя на модель Cuisine , это действительно стыковочный стол для этого M2M. Django ожидает, что таблица соединения будет иметь два поля ForeignKey , один из которых указывает на каждую сторону сустава. Обычно это создаст это для вас, чтобы сохранить здравомыслие. Видимо, тебе не повезло. Поэтому вы должны вручную подключить его.

    Кажется, что поле «GID» является (бесполезным) поле ID для записи, поэтому давайте предположим, что оно является автоматическим приращением целого числа. (Конечно, проверьте команды 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 живет наша кухня типа 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") 

    Джанго знает, к каким полям присоединяться, потому что это объявлено в таблице 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 %} 
    Python - лучший язык программирования в мире.