一 Django模型层简介
Posted 持&恒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一 Django模型层简介相关的知识,希望对你有一定的参考价值。
模型
django提供了一个强大的orm(关系映射模型)系统。
模型包含了你要在数据库中创建的字段信息及对数据表的一些操作
使用模型
定义好模型后,要告诉django使用这些模型,你要做的就是在配置文件中的INSTALLED_APPS中添加模型所在的应用名称
字段类型
模型中的每个字段都是Field类相应的实例,django根据Field类型来确定以下信息:
- 列类型,告知数据库要存储那种数据
- 渲染表单时使用的默认html widget
- 验证,被用在admin和表单中
通用字段参数(常用)
null:如果为True,Django将在数据库中将该字段存储为NULL(如果该字段为空),默认False
blank:如果为True,该字段允许为空值,默认False
注意,null是数据库范畴,blank是表单验证范畴
choices:如果设置了该选项,在渲染HTML时,将会是一个下拉选择框。该选项是一个二元组构成的可迭代对象,选择框中的值就是二元组内的值
如:
YEAR_IN_SCHOOL_CHOICES = (
(‘FR‘, ‘Freshman‘),
(‘SO‘, ‘Sophomore‘),
(‘JR‘, ‘Junior‘),
(‘SR‘, ‘Senior‘),
(‘GR‘, ‘Graduate‘),
)
每个元组中的第一个元素是将被存储在数据库中值,第二个元素由窗体小部件显示
给定一个模型,可以使用get_FOO_display()来访问字段在窗体中的显示值
如:
from django.db import models class Person(models.Model): SHIRT_SIZES = ( (‘S‘, ‘Small‘), (‘M‘, ‘Medium‘), (‘L‘, ‘Large‘), ) name = models.CharField(max_length=60) shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
执行以下代码可分别访问字段的数据库值和显示值:
>>> p = Person(name="Fred Flintstone", shirt_size="L") >>> p.save() >>> p.shirt_size ‘L‘ >>> p.get_shirt_size_display() ‘Large‘
default: 字段的默认值,可以是一个值也可以是一个对象,如果该对象可调用,那么每次创建一新模型对象时它都会被调用
注意,default值是个可调用的对象时,赋值一个对象的引用和调用该对象的区别
help_text:表单部件额外的显示内容,对生成文档也很有用
primary_key:如果为true,该字段就是模型的主键,如果没有指定该选项,会默认生成一个IntergerField的自增ID字段作为主键字段
注意:主键字段时只读的,如果在一个已经存在的对象上面更改主键的值并保存,一个新的对象将会被创建
unique:如果为true,则该字段的值必须唯一
字段别名
除ForeignKey ManyToManyField OneToOneField之外,每个字段都接受一个可选的位置参数(第一个参数),若没有提供该参数,将根据字段名称,将字段名称下划线替换成空格作为别名
ForeignKey ManyToManyField OneToOneField 第一个参数是关联的模型类,使用关键字参数verbose_name指定别名
关系
多对一:如一个汽车厂生产多种汽车,一辆汽车只有一个生产厂家
代码如下:
from django.db import models class Manufacturer(models.Model): # ... pass class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
多对多:一个披萨上可以放多种配料,一种配料可以放在多个披萨上
代码如下:
from django.db import models class Topping(models.Model): # ... pass class Pizza(models.Model): # ... toppings = models.ManyToManyField(Topping)
一般来说ManyToManyField应该放在要在表单中被编辑的对象(如在admin中该字段会被渲染成多选框),如本例:一个披萨选择多种配料
多对多关系的额外字段:throuth
例如:这样一个应用,它记录音乐家所属的音乐小组。 我们可以用一个ManyToManyField
表示小组和成员之间的多对多关系。 但是,有时你可能想知道更多成员关系的细节,比如成员是何时加入小组的。
对于这些情况,Django 允许你指定一个中介模型来定义多对多关系。 你可以将其他字段放在中介模型里面。 源模型的ManyToManyField
字段将使用through
参数指向中介模型。 对于上面的音乐小组的例子,代码如下:
from django.db import models class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): # __unicode__ on Python 2 return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through=‘Membership‘) def __str__(self): # __unicode__ on Python 2 return self.name class Membership(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) group = models.ForeignKey(Group, on_delete=models.CASCADE) date_joined = models.DateField() invite_reason = models.CharField(max_length=64)
应用实例如下:
>>> ringo = Person.objects.create(name="Ringo Starr") >>> paul = Person.objects.create(name="Paul McCartney") >>> beatles = Group.objects.create(name="The Beatles") >>> m1 = Membership(person=ringo, group=beatles, ... date_joined=date(1962, 8, 16), ... invite_reason="Needed a new drummer.") >>> m1.save() >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>]> >>> ringo.group_set.all() <QuerySet [<Group: The Beatles>]> >>> m2 = Membership.objects.create(person=paul, group=beatles, ... date_joined=date(1960, 8, 1), ... invite_reason="Wanted to form a band.") >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
与常规的多对多字段不同,不能使用add()
,create()
或set()
创建关系:
>>> # 下列语句都是无法工作的 >>> beatles.members.add(john) >>> beatles.members.create(name="George Harrison") >>> beatles.members.set([john, paul, ringo, george])
为什么不能这样做? 这是因为你不能只创建 Person
和 Group
之间的关联关系,你还要指定 Membership
模型中所需要的所有信息; 而简单的add
、create
和赋值语句是做不到这一点的。 所以它们不能在使用中介模型的多对多关系中使用。 此时,唯一的办法就是创建中介模型的实例。
remove方法被禁用也是出于同样的原因。 例如,如果通过中介模型定义的表没有在源模型(Group)和目标模型(perseon)
上强制执行唯一性,则remove()
调用将不能提供足够的信息,说明应该删除哪个中介模型实例:
>>> Membership.objects.create(person=ringo, group=beatles, ... date_joined=date(1968, 9, 4), ... invite_reason="You‘ve been gone for a month and we miss you.") >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]> >>> # This will not work because it cannot tell which membership to remove >>> beatles.members.remove(ringo)
但是clear()
方法却是可用的。它可以清空某个实例所有的多对多关系:
>>> # Beatles have broken up >>> beatles.members.clear() >>> # Note that this deletes the intermediate model instances >>> Membership.objects.all()
通过中介模型建立的m2m关系和普通m2m关系,在查询方面是相似的:
>>> Group.objects.filter(members__name__startswith=‘Paul‘) <QuerySet [<Group: The Beatles>]>
也可以利用中介模型的属性查询:
# Find all the members of the Beatles that joined after 1 Jan 1961 >>> Person.objects.filter( ... group__name=‘The Beatles‘, ... membership__date_joined__gt=date(1961,1,1)) <QuerySet [<Person: Ringo Starr]>
如果你需要访问一个成员的信息,你可以直接获取Membership
模型:
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo) >>> ringos_membership.date_joined datetime.date(1962, 8, 16) >>> ringos_membership.invite_reason ‘Needed a new drummer.‘
另一种获取相同信息的方法是,在Person
对象上反向查询:
>>> ringos_membership = ringo.membership_set.get(group=beatles) >>> ringos_membership.date_joined datetime.date(1962, 8, 16) >>> ringos_membership.invite_reason ‘Needed a new drummer.‘
一对一:和其他关系一样,当某个对象扩展自另一个对象时,最常用的方式就是在这个对象的主键上添加一对一关系
模型属性
objects:模型最重要的属性是Manager
。 它是Django 模型进行数据库查询操作的接口,并用于从数据库提取实例。 默认的名称为objects
。 Manager只能通过模型类访问,而不能通过模型实例访问。
模型方法
可以在模型上定义自定义方法来给对象添加自定义底层功能。 Manager
方法用于“表范围”的事务,模型的方法应该着眼于特定的模型实例。这是一个非常有价值的技术,让业务逻辑位于同一个地方 — 模型中。
例如,下面的模型具有一些自定义的方法:
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) birth_date = models.DateField() def baby_boomer_status(self): "Returns the person‘s baby-boomer status." import datetime if self.birth_date < datetime.date(1945, 8, 1): return "Pre-boomer" elif self.birth_date < datetime.date(1965, 1, 1): return "Baby boomer" else: return "Post-boomer"
覆盖预定义的模型方法
models.Model中封装了对数据库的各种操作,特别是,你将要经常改变save()
和delete()
的工作方式
覆盖内建模型方法的一个典型的使用场景是,你想在保存一个对象时做一些其它事情:
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def save(self, *args, **kwargs): do_something() super(Blog, self).save(*args, **kwargs) # Call the "real" save() method. do_something_else()
你还可以阻止保存:
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def save(self, *args, **kwargs): if self.name == "Yoko Ono‘s blog": return # Yoko shall never have her own blog! else: super(Blog, self).save(*args, **kwargs) # Call the "real" save() method
注意:批量操作中被覆盖的模型方法不会被调用
当使用QuerySet批量删除对象或由于级联删除
时,对象的delete()
方法不一定被调用。 为确保自定义的删除逻辑得到执行,你可以使用pre_delete
和/或post_delete
信号。
不幸的是,当批量creating
或updating
对象时没有变通方法,因为不会调用save()
、pre_save
和 post_save
模型继承
在Django 中有3种风格的继承:
- 通常,你只想使用父类来持有一些信息,你不想在每个子模型中都敲一遍。 这个父类永远不会单独使用,所以你要使用抽象的基类
- 如果你继承一个已经存在的模型且想让每个模型具有它自己的数据库表,那么应该使用多表继承。
- 最后,如果你只是想改变一个模块Python 级别的行为,而不用修改模型的字段,你可以使用代理模型。
抽象基类
需要在父类中编写一个Meta类,设置abstract=True,要注意,如果父类和子类有相同的字段名,会出现错误(ps:难道不是重写吗?为毛会报错)
Meta类的继承
如果子类没有声明自己的Meta类, 它将会继承父类的Meta如果子类想要扩展父类的Meta类,它可以子类化它。 例如:
from django.db import models class CommonInfo(models.Model): # ... class Meta: abstract = True ordering = [‘name‘] class Student(CommonInfo): # ... class Meta(CommonInfo.Meta): db_table = ‘student_info‘
注意:abstract属性不会被继承
多表继承
django会在子类中自动创建一个 OneToOneField字段来链接子类和父类
实例:
from django.db import models class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) class Restaurant(Place): serves_hot_dogs = models.BooleanField(default=False) serves_pizza = models.BooleanField(default=False)
Place
里面的所有字段在 Restaurant中也是有效的,只不过没有保存在数据库中的Restaurant
表中。 所以下面两个语句都是可以运行的:
>>> Place.objects.filter(name="Bob‘s Cafe") >>> Restaurant.objects.filter(name="Bob‘s Cafe")
Meta和多表继承
在多表继承中,子类继承父类的 Meta类是没什么意义的。 所有的 Meta选项已经对父类起了作用,再次使用只会起反作用(这与使用抽象基类的情况正好相反,因为抽象基类并没有属于它自己的内容)。
代理模型
有时你可能只想更改 model 在 Python 层的行为实现。比如:更改默认的 manager ,或是添加一个新方法,而这,正是代理继承要做的:为原始模型创建一个代理 。 你可以创建,删除,更新代理 model 的实例,而且所有的数据都可以像使用原始 model 一样被保存。 不同之处在于:你可以在代理 model 中改变默认的排序设置和默认的 manager ,更不会对原始 model 产生影响。声明代理 model 和声明普通 model 没有什么不同。 设置Meta
类中 proxy
的值为 True
,就完成了对代理 model 的声明。
举个例子,假设你想给 Person
模型添加一个方法。 你可以这样做:
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) class MyPerson(Person): class Meta: proxy = True def do_something(self): # ... pass
MyPerson
类和它的父类 Person
操作同一个数据表。 特别的是,Person
的任何实例也可以通过 MyPerson
访问,反之亦然:
>>> p = Person.objects.create(first_name="foobar") >>> MyPerson.objects.get(first_name="foobar")
代理模型管理器:如果你没有在代理模型中定义管理器,代理模型会继承基类管理器,如果代理模型中定义了管理器,它就会变成默认管理器,不过在父类定义的管理器仍然有效
如果你想在代理模型中添加新的管理器,并非替换基类管理器,可以这样,创建一个含有新管理器的基类,作为代理模型的基类放在后面:
# Create an abstract class for the new manager. class ExtraManagers(models.Model): secondary = NewManager() class Meta: abstract = True class MyPerson(Person, ExtraManagers): class Meta: proxy = True
模型继承中隐藏的规则
普通的python类允许子类覆盖父类的任何属性,在Django中,模型字段不允许这样做,如果非抽象基类有一个A字段,那么不能在任何继承自该基类的类中创建A字段。对于抽象基类没有这个限制,抽象基类的子类中会被覆盖,也可以通过设置field_name=None 来删除字段
在包中组织模型
如果有多个模型文件,可以使用一个名为models的python包来替换原有的models.py文件,但是必须将模型文件导入到所在包的__init__.py文件中:
#myapp/models/__init__.py from .organic import Person from .synthetic import Robot
更多细节可参照django内置模块,如django.db.models
以上是关于一 Django模型层简介的主要内容,如果未能解决你的问题,请参考以下文章