简介
模型是你的数据的唯一的、权威的信息源。它包含你所储存数据的必要字段和行为。通常,每个模型对应数据库中唯一的一张表。
基础:
- 每个模型都是django.db.models.Model 的一个Python 子类。
- 模型的每个属性都表示为数据库中的一个字段。
- Django 提供一套自动生成的用于数据库访问的API。
这个例子定义一个Person模型,它有first_name 和last_name 两个属性:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
一些技术上的注意事项:
- 这个表的名称myapp_person,是根据模型中的元数据自动生成的,也可以重写为别的名称。
- id 字段是自动添加的,但这个行为可以被重写。
字段类型
详见博客:Django模型字段类型详解
字段选项
详见博客:Django模型字段选项详解
关系
Django 提供了三种最常见的数据库关系:多对一(many-to-one),多对多(many-to-many),一对一(one-to-one)。
多对一关系
Django 使用 django.db.models.ForeignKey 定义多对一关系。和使用其它字段类型一样:在模型当中把它做为一个类属性包含进来。
ForeignKey 需要一个位置参数:与该模型关联的类。
比如,一辆汽车(Car)有一个制造商(Manufacturer) —— 但是一个制造商(Manufacturer) 生产很多汽车(Car),每一辆汽车(Car) 只能有一个制造商(Manufacturer) —— 使用下面的定义:
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
# ...
多对多关系
ManyToManyField 用来定义多对多关系,用法和其他Field字段类型一样:在模型中做为一个类属性包含进来。
ManyToManyField 需要一个位置参数:和该模型关联的类。
例如,一个披萨可以有多种馅料 ,一种馅料 也可以位于多个披萨上。 如下展示:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
通常,ManyToManyField 实例应该位于可以编辑的表单中。在上面的例子中,toppings 位于Pizza 中(而不是在 Topping 里面设置pizzas 的 ManyToManyField 字段),因为设想一个Pizza 有多种Topping 比一个Topping 位于多个Pizza 上要更加自然。按照上面的方式,在Pizza 的表单中将允许用户选择不同的Toppings。
多对多关系中的其他字段
处理类似搭配 pizza 和 topping 这样简单的多对多关系时,使用标准的ManyToManyField 就可以了。但是,有时你可能需要关联数据到两个模型之间的关系上。
例如,有这样一个应用,它记录音乐家所属的音乐小组。我们可以用一个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)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
在设置中介模型时,要显式地指定外键并关联到多对多关系涉及的模型。这个显式声明定义两个模型之间是如何关联的。
既然你已经设置好ManyToManyField 来使用中介模型(在这个例子中就是Membership),接下来你要开始创建多对多关系。你要做的就是创建中介模型的实例:
>>> 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()
[<Person: Ringo Starr>]
>>> ringo.group_set.all()
[<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()
[<Person: Ringo Starr>, <Person: Paul McCartney>]
与普通的多对多字段不同,你不能使用add、 create和赋值语句(比如,beatles.members = [...])来创建关系:
# THIS WILL NOT WORK
>>> beatles.members.add(john)
# NEITHER WILL THIS
>>> beatles.members.create(name="George Harrison")
# AND NEITHER WILL THIS
>>> beatles.members = [john, paul, ringo, george]
为什么不能这样做? 这是因为你不能只创建 Person和 Group之间的关联关系,你还要指定 Membership模型中所需要的所有信息;而简单的add、create 和赋值语句是做不到这一点的。所以它们不能在使用中介模型的多对多关系中使用。此时,唯一的办法就是创建中介模型的实例。
remove()方法被禁用也是出于同样的原因。但是clear() 方法却是可用的。它可以清空某个实例所有的多对多关系:
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
[]
过创建中介模型的实例来建立对多对多关系后,你就可以执行查询了。 和普通的多对多字段一样,你可以直接使用被关联模型的属性进行查询:
# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
[<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))
[<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.'
一对一关系
OneToOneField用来定义一对一关系。 用法和其他字段类型一样:在模型里面做为类属性包含进来。
当某个对象想扩展自另一个对象时,最常用的方式就是在这个对象的主键上添加一对一关系。
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
def __str__(self): # __unicode__ on Python 2
return "%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(Place, primary_key=True)
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
def __str__(self): # __unicode__ on Python 2
return "%s the restaurant" % self.place.name
class Waiter(models.Model):
restaurant = models.ForeignKey(Restaurant)
name = models.CharField(max_length=50)
def __str__(self): # __unicode__ on Python 2
return "%s the waiter at %s" % (self.name, self.restaurant)
建立好模型之后我们就可以进行数据库操作:
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland')
>>> p2.save()
Meta(元)的选择
使用内部的class Meta 定义模型的元数据,例如:
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
模型元数据是“任何不是字段的数据”,比如排序选项(ordering),数据库表名(db_table)或者人类可读的单复数名称(verbose_name 和verbose_name_plural)。在模型中添加class Meta是完全可选的,所有选项都不是必须的。
更多详细,见博客:Django模型类Meta元数据详解
模型的方法
可以在模型上定义自定义的方法来给你的对象添加自定义的“底层”功能。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"
def _get_full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
full_name = property(_get_full_name)
这个例子中的最后一个方法是一个属性。
重写预定义的模型方法
还有另外一部分封装数据库行为的模型方法,你可能想要自定义它们。特别是,你将要经常改变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.
必须要记住调用超类的方法—— super(Blog, self).save(*args, **kwargs) —— 来确保对象被保存到数据库中。如果你忘记调用超类的这个方法,默认的行为将不会发生且数据库不会有任何改变。
还要记住传递参数给这个模型方法 —— 即*args, **kwargs。 Django 未来将一直会扩展内建模型方法的功能并添加新的参数。如果在你的方法定义中使用*args, **kwargs,将保证你的代码自动支持这些新的参数。
批量操作中被覆盖的模型方法不会被调用
注意,当使用查询集批量删除对象时,将不会为每个对象调用delete() 方法。为确保自定义的删除逻辑得到执行,你可以使用pre_delete 和/或post_delete 信号。
不幸的是,当批量creating 或updating 对象时没有变通方法,因为不会调用save()、pre_save和 post_save。
来源:oschina
链接:https://my.oschina.net/u/2772739/blog/751340