1、动态条件查询
简单: design_list = Design.objects.filter(Q(tags__tag__contains = "tag1") and Q(tags__tag__contains = "tag2")
条件特别多,数量不定,动态的怎么办?
tag_list = ['tag1', 'tag2', 'tag3']
base_qs = Design.objects.all()
for t in tag_list:
base_qs = base_qs.filter(tags__tag__contains=t)
以上,其实是 and条件, .filter( Q () & Q() )
想用or 要这样写:
tags = ['tag1', 'tag2', 'tag3'] q_objects = Q() # Create an empty Q object to start with for t in tags: q_objects |= Q(tags__tag__contains=t) # 'or' the Q objects together designs = Design.objects.filter(q_objects)
tags = ['tag1', 'tag2', 'tag3'] q_objects = Q() # Create an empty Q object to start with for t in tags: q_objects |= Q(name=t) # 'or' the Q objects together print(str(Student.objects.values('name').filter(q_objects).query))
输出: SELECT "d3_student"."name" FROM "d3_student" WHERE ("d3_student"."name" = tag1 OR "d3_student"."name" = tag2 OR "d3_student"."name" = tag3)
除了 |= 还有 &= 看如下例子
q_objects = Q() for f in self.get_form_fields(): if self.form.cleaned_data.get(f): q_objects &= Q(**{f+'__icontains':self.form.cleaned_data[f]})
2、group by (annotate, aggregate)
(1) aggregate后,变为dict,不是queryset
>>> from django.db.models import Avg, Max, Min, Sum, Count
>>> User.objects.all().aggregate(Avg('id'))
{'id__avg': 7.571428571428571}
>>> User.objects.all().aggregate(Max('id'))
{'id__max': 15}
>>> User.objects.all().aggregate(Min('id'))
{'id__min': 1}
>>> User.objects.all().aggregate(Sum('id'))
{'id__sum': 106}
(2)annotate是增加新的一列,结果仍是queryset
class Author(models.Model): name = models.CharField(max_length=100) class Book(models.Model): name = models.CharField(max_length=300) rating = models.FloatField() authors = models.ManyToManyField(Author) Author.objects.values('name').annotate(average_rating=Avg('book__rating'))
上面例子, 相当于最传统的guoup : select auther.name, Avg(book.rating) from auther, book where auther.id=book.author_id group by auther.name
会合并相同的name,计算rating的平均值。 group by 一定是被查询字段的非计算列
a=AccessAttempt.objects.values('ip_address').annotate(sum_times=Sum('failures_since_start'))
print(a)
输出:<QuerySet [{'ip_address': '127.0.0.1', 'sum_times': 5}]>
a = AccessAttempt.objects.filter(ip_address='127.0.0.1').aggregate(sum_times=Sum('failures_since_start')) print(a) print(a['sum_times'])
输出:
{'sum_times': 5}
5
再看看下面例子:
mysql> select * from foo_personmodel; +----+-------+-----+ | id | name | age | +----+-------+-----+ | 1 | tim | 16 | | 2 | jerry | 27 | | 3 | marry | 18 | | 4 | tim | 20 | | 5 | alis | 17 | +----+-------+-----+ 5 rows in set (0.00 sec)
orm
PersonModel.objects.values("name") <QuerySet [{'name': 'tim'}, {'name': 'jerry'}, {'name': 'marry'}, {'name': 'tim'}, {'name': 'alis'}]>
sql
mysql> select name,count(id) as counts from foo_personmodel group by name; +-------+--------+ | name | counts | +-------+--------+ | tim | 2 | | jerry | 1 | | marry | 1 | | alis | 1 | +-------+--------+ 4 rows in set (0.00 sec)
orm
PersonModel.objects.values("name").annotate(counts=Count(id)) <QuerySet [{'name': 'tim', 'counts': 2}, {'name': 'jerry', 'counts': 1}, {'name': 'marry', 'counts': 1}, {'name': 'alis', 'counts': 1}]>
当 values 和 annotate 一起使用的时候,values 就承担起了 group by 的角色。并且自动去掉了重项
3、如何确保table只有一行数据
name = models.CharField(max_length=100)
def save(self, *args, **kwargs):
if self.__class__.objects.count():
self.pk = self.__class__.objects.first().pk
super().save(*args, **kwargs)
PK设置相同的值,就不会add,而是update .
如何复制或者克隆一行数据
In [2]: Hero.objects.all().count() Out[2]: 4 In [3]: hero = Hero.objects.first() In [4]: hero.pk = None In [5]: hero.save() In [6]: Hero.objects.all().count() Out[6]: 5
4、insert一个table时候,自动更新其他table
class Category(models.Model):
name = models.CharField(max_length=100)
hero_count = models.PositiveIntegerField()
villain_count = models.PositiveIntegerField()
class Meta:
verbose_name_plural = "Categories"
class Hero(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
class Villain(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
自动更新举例:
class Hero(models.Model): def save(self, *args, **kwargs): if not self.pk: Category.objects.filter(pk=self.category_id).update(hero_count=F('hero_count')+1) super().save(*args, **kwargs) class Villain(models.Model): def save(self, *args, **kwargs): if not self.pk: Category.objects.filter(pk=self.category_id).update(villain_count=F('villain_count')+1) super().save(*args, **kwargs)
注意: 直接self.category.hero_count += 1 这样写的话,update操作也会+1
如果用signal,相同逻辑可以这样写:
from django.db.models.signals import pre_save from django.dispatch import receiver @receiver(pre_save, sender=Hero, dispatch_uid="update_hero_count") def update_hero_count(sender, **kwargs): hero = kwargs['instance'] if hero.pk: Category.objects.filter(pk=hero.category_id).update(hero_count=F('hero_count')+1) @receiver(pre_save, sender=Villain, dispatch_uid="update_villain_count") def update_villain_count(sender, **kwargs): villain = kwargs['instance'] if villain.pk: Category.objects.filter(pk=villain.category_id).update(villain_count=F('villain_count')+1)
那么, 使 扩展save()还是用signal呢? 建议扩展save(), 简单好理解。 但是如果涉及第三方app更新这个table,就只能用signal了
5、扩展定制truncate
普通删除可以
>>> Category.objects.all().count()
7
>>> Category.objects.all().delete()
(7, {'entity.Category': 7})
>>> Category.objects.all().count()
0
以上就是DELETE FROM ... , 但是如果是海量数据表,就会很慢,可以用如下方法
class Category(models.Model): # ... @classmethod def truncate(cls): with connection.cursor() as cursor: cursor.execute('TRUNCATE TABLE "{0}" CASCADE'.format(cls._meta.db_table))
可以Category.truncate()来删除数据。
6、How to convert string to datetime and store in database
日期字符串不能直接存储在date field字段,可以这样做
>>> user = User.objects.get(id=1)
>>> date_str = "2018-03-11"
>>> from django.utils.dateparse import parse_date // Way 1
>>> temp_date = parse_date(date_str)
>>> a1 = Article(headline="String converted to date", pub_date=temp_date, reporter=user)
>>> a1.save()
>>> a1.pub_date
datetime.date(2018, 3, 11)
>>> from datetime import datetime // Way 2
>>> temp_date = datetime.strptime(date_str, "%Y-%m-%d").date()
>>> a2 = Article(headline="String converted to date way 2", pub_date=temp_date, reporter=user)
>>> a2.save()
>>> a2.pub_date
datetime.date(2018, 3, 11)
7、使用uuid,代替id做为pk, 因为Id默认是数字,容易被记住和猜到,所以用uuid
import uuid
from django.db import models
class Event(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
details = models.TextField()
years_ago = models.PositiveIntegerField()
>>> eventobject = Event.objects.all()
>>> eventobject.first().id
'3cd2b4b0c36f43488a93b3bb72029f46'
8、