带有多表查询的 SQL Join 版本的 Djangoviews.py

2024-04-29

需要一些有关 Django 版本的 SQL 多表查询的帮助。该查询使用 3 个表来检索餐厅名称、地址Restaurants table和美食类型来自Cuisinetypes table。所有这些都基于通过 URL 传递的菜品名称,菜品 ID 存储在 Cuisine 表中。

模型.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 %}

好吧,这些是一些不清楚的表和字段名称,但我可以说,查询看起来像这样:

(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”字段是记录的(无用的)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模型。请注意更正后的驼峰式类名:

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 or db_column东西,所以我冒险猜测 Django 正在创建它。这意味着我们可以让它创建id字段对我们来说,我们可以从我们的代码中省略它。 (如果情况并非如此,那么我们只需像其他模型一样添加它。但是您确实不应该有可为空的记录 ID。)这就是我们的美食类型ManyToManyField lives:

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

The 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 将运行两个查询:一个与您的查询类似,用于获取联合 ID,另一个查询用于获取相关的 ID。CuisineType记录。那么通过查询结果访问就不需要返回数据库了。

最后两件事是顺序:

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

And the 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 %}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

带有多表查询的 SQL Join 版本的 Djangoviews.py 的相关文章

随机推荐

  • “该选择不能在任何服务器上运行”

    我一直在 Eclipse 中开发一个动态 Web 项目 我收到这个错误 该选择不能在任何服务器上运行 早些时候它工作得很好 但现在我收到了这个错误 我删除了服务器并再次添加 Project gt Right Click gt Propert
  • 错误:$.cookie 不是函数

    我使用以下代码添加了 jQuery 插件 我的激活cookie的代码如下 document ready function ul sub menu a click function sliderid prodcls css display n
  • 我的项目中是否会自动创建 gitignore 文件?

    我一直在尝试使用 EGit 管理我正在处理的项目的本地副本 一旦其他人推我 我就会拉 但是 有文件显示我在本地存储库中有未提交的更改 但我没有进行任何更改 我尝试通过手动识别这些更改来解决这些问题 并发现它们来自 gitignore 文件
  • pytube 在 Android 中传输视频所需的时间太长

    我在用pytube在 Android 中流式传输视频 借助chaquopy 视频文件 py from pytube import YouTube def video link yt YouTube f https www youtube c
  • FormsAuthentication.SetAuthCookie() 是否需要重定向?

    检查用户的凭据并确认其良好后 我正在使用FormsAuthentication SetAuthCookie Username false 对用户进行身份验证 然后在母版页中我使用Page User Identity IsAuthentica
  • 不区分大小写“包含(字符串)”

    有没有办法让下面的返回为真 string title ASTRINGTOTEST title Contains string 似乎没有过载允许我设置区分大小写 目前我将它们都大写 但这很愚蠢 我指的是i18n http en wikiped
  • 为什么这个 eclipse 错误显示以及它的解决方案应该是什么

    缺少库 xdoclet 1 2 1 jar 选择 XDoclet 的主目录 1 2 1 为什么这个 eclipse 错误显示以及它的解决方案应该是什么alz 这可能是因为该 jar 没有添加到您的项目构建路径中 请按照以下步骤操作 Righ
  • 如何处理MaxUploadSizeExceededException

    MaxUploadSizeExceededException当我上传的文件大小超过允许的最大值时 会出现异常 我想在出现此异常时显示错误消息 如验证错误消息 我该如何处理这个异常 以便在 Spring 3 中执行类似的操作 Thanks 这
  • 可变长度数组性能影响 (C/C++)

    我正在编写一个相当简单的函数 它将数组发送到文件描述符 但是 为了发送数据 我需要附加一个一字节标头 这是我正在做的事情的简化版本 它似乎有效 void SendData uint8 t buffer size t length uint8
  • 使用Javascript自动将光标移动到下一个表单字段[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 是否可以使用Javascipt自动将
  • 自动滚动的实时行情(如 Twitter 的“热门推文”)

    是否有一个好的 Javascript 库可以生成自动滚动列表 例如 Twitter 主页上的 热门推文 最好作为 jQuery 插件 它还应该支持 AJAX 功能 动态添加新列表项 由于我们对现有的解决方案并不满意 因此我们从头开始实施了一
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE
  • 如何在 VMWare 中查看 Django 开发服务器?

    我正在尝试从运行 Windows 的 VMWare 连接到在 OS X localhost 8000 中运行的 Django 开发服务器 我尝试了各种设置组合 但似乎都不起作用 在 localhost 8000 上运行 Django 服务器
  • ServiceM8 - 我可以过滤多个字段吗

    我正在研究与 ServiceM8 的集成 并希望使用 filter REST API 来过滤多个 类似 company uuid 和 status 之类的东西 ServiceM8 文档给出了以下示例 但不包括 OR AND 类型逻辑的任何使
  • 在 where 子句中使用 CASE 时出现语法错误

    类似的问题已被问到here https stackoverflow com questions 22001787 using if condition in where clause 无论如何 我遇到了语法错误 我无法弄清楚 这是我的代码
  • 包含不同类型的两个集合相交

    假设我有一个集合 称之为ids它是类型IEnumerable
  • 如何在 Java 中以编程方式获取接口的所有实现的列表?

    我可以通过反思或类似的方式来做到这一点吗 我已经搜索了一段时间 似乎有不同的方法 这里总结一下 反思 https github com ronmamo reflections如果您不介意添加依赖项 该库非常受欢迎 它看起来像这样 Refle
  • 如何在亚马逊云(AWS EC2)中安装firefox?

    我有一个拥有所有权限的 AWS 账户 我想在环境中安装 Firefox 因为我的应用程序将启动 Firefox 并对 Web 应用程序运行一些测试 如何安装火狐浏览器 执行以下命令解决了问题 sudo apt get install xvf
  • 从多个通知启动活动会覆盖之前的意图

    public static void showNotification Context ctx int value1 String title String message int value2 NotificationManager no
  • 带有多表查询的 SQL Join 版本的 Djangoviews.py

    需要一些有关 Django 版本的 SQL 多表查询的帮助 该查询使用 3 个表来检索餐厅名称 地址Restaurants table和美食类型来自Cuisinetypes table 所有这些都基于通过 URL 传递的菜品名称 菜品 ID