Django的数据库管理
Django模型关系
在模型中定义关系时(即,ForeignKey,OneToOneField或ManyToManyField),该模型的实例将具有访问相关对象的便捷API。
例如,在使用Django官方的Blog-Entry模型,Entry对象e可以通过访问blog属性获取其关联的Blog对象:e.blog。Django还为关系的“其他”方创建API访问器 - 从相关模型到定义关系的模型的链接。 例如,Blog对象b可以通过entry_set属性访问所有相关Entry对象的列表:b.entry_set.all()。
一对多关系
Topic和Post模型是One-to-many
关系:
class Topic(models.Model):
"""主题"""
id = models.AutoField(primary_key=True)
text = models.CharField(max_length=20)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='the owner of this topic')
class Meta:
app_label = 'learning_logs'
def __str__(self):
return self.text
class Post(models.Model):
"""同一Topic下的多篇文章"""
# many to one relationship
id = models.AutoField(primary_key=True)
topic = models.ForeignKey(Topic, on_delete=models.CASCADE, verbose_name='the related topic')
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='the owner of the post')
# text = models.TextField('post content')
text = MDTextField()
html_content = models.TextField('html content', blank=True)
date_added = models.DateTimeField(auto_now_add=True)
date_edit = models.DateTimeField(auto_now=True)
# Model metadata is “anything that’s not a field”, such as ordering options (ordering),
# database table name (db_table),
# or human-readable singular and plural names (verbose_name and verbose_name_plural).
# None are required, and adding class Meta to a model is completely optional.
class Meta:
verbose_name_plural = 'posts'
app_label = 'learning_logs'
def __str__(self):
return self.text[:50] + '...'
如果模型定义了ForeignKey
,可以通过这个模型的实例来访问与之联系的模型对象,如:
>>> from learning_logs.models import Post
>>> p = Post.objects.get(id=3)
>>> p.topic
<Topic: Seem tonight begin rock. Daughter beyond better network magazine. Then knowledge wind alone.
Left themselves public impact possible figure animal. May professional tax religious. Product shake force.>
可以将外键设为None
来取消关联(外键要允许为空,null=True
):
>>> p.topic=None
>>> p.save()
在第一次访问相关对象时,将缓存对一对多关系:
>>> p = Post.objects.get(id=3)
>>> print(p.topic) # 会查询数据库
>>> print(p.topic) # 使用缓存
select_related()
方法以递归的方式预先填充所有一对多关系的缓存:
>>> p = Post.objects.select_related().get(id=4)
>>> print(p.topic) # 会使用缓存
Suffer meeting customer anything. If run town ask win opportunity. Bit itself on throughout.
Administration security story wife actually. Take place arm attorney.
>>>
如果模型具有ForeignKey
,则外键模型的实例将可以访问返回第一个模型的所有实例的Manager。 默认情况下,此Manager名为FOO_set,其中FOO是源模型名称,小写。 此Manager返回QuerySets,可以进行筛选、排序等操作。
>>> topic = Topic.objects.get(id =8)
>>> topic.post_set.all() # Returns all post objects related to Topic.
<QuerySet [<Post: Piece check system kind sister if listen. Among mo...>, <Post: Various sport director area. Up receive real stree...>, <Post: Rise star stay heavy say today rich born.
Board wh...>]>
>>> topic.post_set.count()
3
处理关联对象的方法:
add(obj1, obj2,...)
: 将指定的模型对象添加到关联的对象集合;create(**kwargs)
:创建新对象;remove(obj1, obj2, ...)
:从集合中删除关联的对象;clear()
:清除所有关联对象;set(objs)
:用新对象替换旧的关联对象集合;
b = Blog.objects.get(id=1)
b.entry_set.set([e1, e2])
多对多关系
如下,有2个模型:Topic和ExampleModel,
class Topic(models.Model):
"""主题"""
id = models.AutoField(primary_key=True)
text = models.CharField(max_length=20)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='the owner of this topic')
class Meta:
app_label = 'learning_logs'
def __str__(self):
return self.text
class TestModel(models.Model):
topic = models.ManyToManyField(Topic)
headline = models.CharField(max_length=255)
def __str__(seld):
return self.headline
要为加多对多关系添加记录,需使用add
方法:
>>> from learning_logs.models import Topic, TestModel
>>> topic = Topic.objects.get(id =2)
>>> test = TestModel.objects.create(headline='ManyToMany')
>>> test.topic.add(topic)
为test对象添加多个topic:
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(id=3)
>>> topics = Topic.objects.filter(owner=user)
>>> test.objects.add(*topics)
>>>
反向添加,为topic添加test:
>>> test2 = TestModel(headline='many to many')
>>> test2.save()
>>> topic = Topic.objects.get(id = 22085)
>>> topic.testmodel_set.add(test, test2)
观察数据库,生成了2张表:testmodel表和记录2个模型关系的表testmodel_topic.
在test模型表中只有headline字段。
在它们的关系表中,可以查询到testmodel模型对象和topic模型对象的关系:
一对一关系
一对一关系和一对多关系很相似,在模型中使用OneToOneField
后,这个模型的实例就能通过简单的方式访问关联的模型对象。
from django.contrib.auth.models import User, AbstractUser
class UserProfile(models.Model):
"""为已经使用了内置User模型的项目来拓展用户模型 """
# user对象可使用 user.userprofile, 或 user.proflie 得到用户信息
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
location = models.CharField('地址', max_length=64, blank=True)
tel = models.CharField('联系方式', max_length=50, blank=True)
about_me = models.TextField('关于我')
mod_date = models.DateTimeField('上次修改日期', auto_now=True)
avatar = models.ImageField('用户头像', upload_to='avatar/%Y/%m/%d', default='avatar/wenhuang.jpg',blank=True, null=True)
class Meta:
verbose_name = "用户信息"
app_label = 'users'
def __str__(self):
return f"{self.user.__str__()} profile"
因为是一对一
关系,可以直接选择关联的模型对象:
>>> from users.models import User, UserProfile
>>> profile = UserProfile.objects.get(id=3)
>>> user = profile.user
>>> user.id
1
Manager
Manager
类是为Django模型提供数据库查询操作的接口。对于Django应用程序中的每个模型,至少存在一个Manager。Manager类的工作方式记录在Making queries中
默认情况下,Django为每个Django模型类添加一个名为objects的Manager。 但是,如果要将对象用作字段名称,或者如果要使用Manager的对象以外的名称,则可以基于每个模型对其进行重命名。 要为给定类重命名Manager,请在该模型上定义models.Manager()类型的类属性。 例如:
from django.db import models
class Person(models.Model):
#...
people = models.Manager()
通过扩展基本Manager类并在模型中实例化自定义Manager,可以在特定模型中使用自定义Manager。自定义Manager可能有两个原因:添加额外的Manager方法,或修改Manager返回的初始QuerySet。
添加额外的Manager方法
添加额外的Manager方法是向模型添加“表级”功能的首选方法。自定义的Manager方法可以返回想要的任何内容,它不必返回QuerySet。
from django.db import models
class PollManager(models.Manager):
def with_counts(self):
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("""
SELECT p.id, p.question, p.poll_date, COUNT(*)
FROM polls_opinionpoll p, polls_response r
WHERE p.id = r.poll_id
GROUP BY p.id, p.question, p.poll_date
ORDER BY p.poll_date DESC""")
result_list = []
for row in cursor.fetchall():
p = self.model(id=row[0], question=row[1], poll_date=row[2])
p.num_responses = row[3]
result_list.append(p)
return result_list
class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
poll_date = models.DateField()
objects = PollManager()
class Response(models.Model):
poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
person_name = models.CharField(max_length=50)
response = models.TextField()
PollManager
提供了一个方法with_counts()
,该方法返回一个包含所有OpinionPoll对象的列表,每个对象都有一个额外的num_responses
属性,该属性是聚合查询count的结果。可以使用OpinionPoll.objects.with_counts()
方法来返回一个包含num_responses
属性的OpinionPoll
对象列表。
修改manager返回的查询集
默认情况下,Manger会返回所有查询到的对象。下面2个Managers,一个会返回所有的对象,一个会筛选出作者为Roald Dahl的对象。
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # The default manager.
dahl_objects = DahlBookManager() # The Dahl-specific manager.
get_queryset()
返回的是查询结果集合,可以使用filter
、exclude
等方法。
也可以在一个模型下定义多个Manager,变相的提供了筛选:
class AuthorManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(role='A')
class EditorManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(role='E')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
people = models.Manager()
authors = AuthorManager()
editors = EditorManager()
使用自定义的QuerySet方法
在自定义的Manger里可以使用自定义的查询方法:
class PersonQuerySet(models.QuerySet):
def authors(self):
return self.filter(role='A')
def editors(self):
return self.filter(role='E')
class PersonManager(models.Manager):
def get_queryset(self):
return PersonQuerySet(self.model, using=self._db)
def authors(self):
return self.get_queryset().authors()
def editors(self):
return self.get_queryset().editors()
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
people = PersonManager()
这样可以直接使用Person.people.author()
或是Person.people.editors()
方法了。
继承使用自定义的Manger的模型
可以创建一个模型的抽象类,它使用自定义的Manger方法,通过继承这个抽象类,模型都能使用自定义的Manager方法。
class AbstractBase(models.Model):
# ...
objects = CustomManager()
class Meta:
abstract = True
class ChildA(AbstractBase):
# ...
# This class has CustomManager as the default manager.
pass
class ChildB(AbstractBase):
# ...
# An explicit default manager.
default_manager = OtherManager()