python - 带有多表查询的SQL的Django views.py版本

标签 python html mysql sql django

需要一些有关Django版本的SQL多表查询的帮助。查询使用3个表从Restaurants table检索餐厅名称、地址和菜系类型。所有这些都基于通过URL传递的菜系名称,菜系ID存储在菜系表中。
模型.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'

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

视图.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 %}

最佳答案

好吧,这些是一些不清楚的表和字段名,但我能告诉您的是,查询看起来像:

(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子句,发现我们需要一个连接。要进行联接,必须在一个联接模型中声明一个关系字段(Django将添加一个反向关系,我们应该命名它)。所以我们将cuisine.cuisineid与“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字段通常仅命名为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模型中。请注意更正的camel case类名:
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_tabledb_column的东西,所以我觉得Django正在创建它。这意味着我们可以让它为我们创建id字段,我们可以从代码中省略它。(如果不是这样的话,我们只是像添加其他模型一样添加它。但你真的不应该有一个可为空的记录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中的join看起来就像是通过relation字段取消引用。在查询中,这意味着使用双下划线连接相关字段名:
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()

你需要把菜品类型拉进来展示。结果发现,您的查询在那里效率很低,因为它只会将您带到join表,您需要运行进一步的查询来获取相关的CuisineType记录。猜怎么着:Django有你的保险。
(Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
    .prefetch_related("cuisine_types"))

Django将运行两个查询:一个类似于您的查询以获取联合id,另一个类似于您的查询以获取相关的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 - 带有多表查询的SQL的Django views.py版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22312873/

相关文章:

php - 未找到翻译类别

python - Sklearn.KMeans() : Get class centroid labels and reference to a dataset

python - 使用 PIL 加载 RGBA 位图

python - 使用 Raspberry pi3 将传感器数据保存在 CSV 文件中

javascript - 对不同的 HTML 文档元素使用具有相同规则名称的不同 CSS 表

html - 为什么我的按钮在我向容器添加填充后不起作用?

mysql - SQL - Java/Eclipse - UPDATE 语句查询 [重新询问,插入更多代码]

python - 检查子图是否是 NetworkX 中的集团的最快方法

javascript - Navgoco 不是影响密码保护页面的功能

php - 在 WordPress 中搜索自定义表