将 SQL 转换为 Django 可以使用的东西
Posted
技术标签:
【中文标题】将 SQL 转换为 Django 可以使用的东西【英文标题】:Converting SQL to something that Django can use 【发布时间】:2022-01-21 07:06:18 【问题描述】:我正在努力将一些相对复杂的 SQL 转换为 Django 可以使用的东西。我试图不只使用原始 SQL,因为我认为使用标准 Django 工具包将帮助我了解更多关于 Django 的信息。
我已经设法将部分 sql 分解成块,并且正在零碎地处理它们以使事情变得更容易。
这是有问题的 SQL:
SELECT i.year, i.brand, i.desc, i.colour, i.size, i.mpn, i.url,
COALESCE(DATE_FORMAT(i_eta.eta, '%M %Y'),'Unknown')
as eta
FROM i
JOIN i_eta ON i_eta.mpn = i.mpn
WHERE category LIKE 'kids'
ORDER BY i.brand, i.desc, i.colour, FIELD(size, 'xxl','xl','l','ml','m','s','xs','xxs') DESC, size+0, size
这是我所拥有的(尝试逐行转换):
(grabbed automatically when performing filters)
(have to figure out django doc on coalesce for syntax)
db alias haven't been able to find yet - it is crucial since there is a db view that requires it
already included in the original q
.select_related?
.filter(category="kids")
.objects.order_by('brand','desc','colour') - don't know how to deal with SQL FIELDS
任何建议将不胜感激!
【问题讨论】:
1) 你可以改变表格的结构吗? 2) 您是在寻找惯用 Django 的东西,还是希望尽可能少地更改上述语句的逻辑? 我对 Django 的了解还不够,无法知道最好的做法是什么。带有关联视图的 SQL 查询运行良好且速度非常快。我想知道在 Django 中做到这一点的“最佳”方式(不管是什么意思)。如果有“最佳实践”可以将 SQL 语句转换为产生类似结果的东西,那么我会这样做。 【参考方案1】:这是我的结构。
首先,我假设您的 i 和 i_eta 模型如下所示:
class I(models.Model):
mpn = models.CharField(max_length=30, primary_key=True)
year = models.CharField(max_length=30)
brand = models.CharField(max_length=30)
desc = models.CharField(max_length=100)
colour = models.CharField(max_length=30)
size = models.CharField(max_length=3)
class IEta(models.Model):
i = models.ForeignKey(I, on_delete=models.CASCADE)
eta = models.DateField()
一般想法:
在 Django 中编写合并:我不会在 ORM 中用“未知”替换空值。这是表示层的问题:应该在模板中处理。
对于日期格式,您可以在 Python 中进行日期格式。
不确定数据库别名是什么。
要同时使用多个表,您可以使用 select_related()、prefetch_related() 或什么都不做。
-
select_related() 将执行连接。
prefect_related() 将从第一个查询集中获取外键 ID,然后生成类似
SELECT * FROM table WHERE id in (12, 13, 14)
的查询。
什么都不做会自动工作,但有SELECT N+1 problem的缺点。
我通常更喜欢 prefetch_related()。
要自定义大小字段的排序顺序,您有三个选项。我的偏好是选项 1,但是这三个中的任何一个都可以。
-
Denormalize the sort criteria。添加一个名为 size_numeric 的新字段。覆盖 save() 方法以在保存新实例时填充此字段,给 xxl 值 1,给 xl 值 2,等等。
Sort in Python。本质上,您使用 Python 的内置排序方法进行排序,而不是在数据库中对其进行排序。如果您有数千个结果,这将无法正常工作。
Invoke the mysql function。本质上,使用 annotate(),您可以将函数的输出添加到查询集中。 order_by() 可以按该函数排序。
【讨论】:
@dukehenry 如果您的模型缺少主键,Django 会自动添加一个 id 字段。见docs.djangoproject.com/en/4.0/topics/db/models/… 谢谢,我刚刚在文档中找到了。除了使用 mpn = models.CharField(primary_key=True, max_length=50) 之外,我还有什么方法可以定义我的模型吗? @dukehenry 是的,没错。 Django 确实要求模型有一个主键。如果 mpn 满足作为主键的条件,则无需添加额外的主键。 我已经取得了一些成功,并且可以尝到进步的滋味。我创建了 2 个单独的查询,这些查询给了我有用的结果,但需要将它们合并在一起并遇到了麻烦。 1. (base) query = i.objects.filter(category='kids').values('year', 'brand', 'desc', 'color', 'size', 'mpn', 'url') .order_by('brand','desc','colour') 2. (view) query = i_Eta.objects.values('eta') 3. (combined) query = i.objects.filter(category='kids' ).select_related('i_Eta', 'eta').values('year', 'brand', 'desc', 'color', 'size', 'mpn', 'url').order_by('brand', 'desc','color') @dukehenry 我建议你不要使用 .values(),因为它比字典更容易操作 ORM 对象。例如,您可以获得对外键引用的对象的引用。如果出于性能原因需要限制选择,我会使用 .only() 代替。以上是关于将 SQL 转换为 Django 可以使用的东西的主要内容,如果未能解决你的问题,请参考以下文章