model是对于信息的一种模型封装与定义。它包含了你要存储的必要字段和操作数据的方法。一句话概括就是,每个模型映射了一张数据表。
基本概念:
每个model都是继承于django.db.models.Model
的Python类。
model的每一个属性对应数据表中的一个字段。
通过所有的这些,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)
其中first_name
和last_name
是model的字段。如你所见,每一个字段被定义为class类的一个属性,而每个属性对应着数据库的一列。
上面的创建Person
,model模型的过程用SQL语句翻译过来如下:
CREATE TABLE myapp_person(
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
下面是一些需要注意的问题:
数据表的名字,myapp_person
,自动继承一些model的metadata设定,但是同时支持自定义。
id字段是自动添加的,但是它同样可以自定义。
CREATE TABLE
这个SQL语句在这个例子中由PostgreSQL
来实现,
一旦你定义好了你的模型,你需要告诉Django你将要使用这些models。这就需要秀修改工程目录下的settings.py
文件。假设你的model定义在了app名为myapp的models.py
文件中,为了使得Django识别出你要使用这个model,你就需要在settings.py
中设定如下:
INSTALLED_APPS = [
#...
'myapp',
#...
]
当你添加新的APP到INSTALLED_APPS
中时。需要运行命令python manage.py migrate
使设置生效。可供选择的是为了使用git等代码管理软件,你可以先运行python manage.py makemigrations
。
model最重要的部分而且也是model所需的基础部分是它定义的数据库字段的集合。字段由类的属性来定义。需要注意一点,不要使用和models API冲突的名字来命名字段例如clean,save
或者delete
。
例子:
from django.db import models
class Musician(models.Model):
first_name = models.CharField(max)length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician,on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DataField()
num_stars = models.IntegerField()
你模型中的每一个字段应该是一个Field
类的实例。Django使用字段类来决定一些事:
列的类型,就是告诉数据库要存储的数据类型是什么。
默认的HTML插件 ,用以渲染表单字段(例如,
)
基本的验证需求,在Django的admin中和自动生成的表单中使用。
Django自带了很多内建的字段类型。若Django没有你想要的类型,你可以自己实现。
每一个字段使用一个确定的字段声明参数集合。例如,CharField
(还有它的子类)需要一个max_length
参数来声明数据库用于存储字段VARVHAR
的个数。
同样的,还有其他的一些选项可用来设置字段,它们都是可选的。下面介绍几个比较常用的设置选项:
null
:
若为True,Django会把空数据使用NULL存储在数据库中。默认是False。
blank
:
若为True,该字段允许为空。默认是False。
注意它和null
的不同。null
是纯粹和数据库相关的,而’blank’则是和验证相关的。若一个字段的blank=True
,表单的验证将会允许实例带一个空值。反之则不行。
choices
:
一个可迭代的元祖,用来作为字段内容的选择。若这个给定,默认的表单插件将会变成一个单选框而不是简单的文本字段,并且单选框中的选项数目由给定的choices
来限定。
一个标准的choices列表和下面的形式类似:
YEAR_IN_SCHOOL_CHOICES = (
('FR','Freshman'),
('SO','Sophmore'),
('JR','Junior'),
('SR','Senior'),
('GR','Graduate'),
)
每个元组中的第一个元素是要存储在数据库中的内容。第二个元素用于在显示的控件上展示。
给定一个model的实例,用于显示的choices的值可以通过使用get_FOO_display()
方法来获取,例如:
from django.db import models
class Person(models.Model):
SHIRT_SIZE = (
('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()
'L'
default
:
这个选项用于设置该字段的默认值。可以是一个值或者可以是一个可以调用的对象。若是可调用的对象,它会在每次新对象创建的时候调用。
help_text
:
额外的帮助文本用于显示在widget
上。它对文档的生成很有用。
primary_key
:
若为True,该字段会作为这个model的主键。如果你没有为其他字段声明primary_key=True
,Django会自动地添加一个IntegerField
字段作为主键。所以如果没有特殊需求,这个选项可以不做设置。
主键的字段是只读的。如果你改变了现有对象的主键的值然后保存了这个对象,一个新的对象就会和旧的对象并行创建。啥意思呢?如下面的例子所示:
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=100,primary_key=True)
>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
'Apple','Pear']>
unique
:
若为True,该字段必须是整张表中独一无二的
默认情况下,Django给每个模型以下字段:
id = models.AutoField(primary_key=True)
这是一个自动添加的自增主键。
如果你想声明一个典型的主键,只需要在对应的字段选项中设置primary_key=True
。若Django看到你显式声明了自定义的主键,那么Django就不会为你创建一个自增的id字段。
每个模型需要明确一个字段作为主键。
除了ForeignKey
,ManyToManyField
和OneToOneField
,每个字段都有一个可选的设置参数:详细。若这个选项未给定,Django会使用属性名来定义,用下划线分隔。
下面的例子中,verbose的名称是”person’s first name”
first_name = models.CharField("person's first name",max_length=30)
下面的例子中,verbose的值为”first name”:
first_name = models.CHarField(max_length=30)
ForeignKey
,ManyToManyField
和OneToOneField
需要第一个参数为model类对象,所以如果要使用verbose_name
,需要显式地声明:
poll = models.ForeignKey(
Poll,
on_delete=models.CASCADE,
verbose_name="the related ,
)
sites = models.ManyToManyField(
Site,
verbose_name="list of sites"
)
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
verbose_name="releated place",
)
一个惯例就是verbose_name
的第一个字母一般不写成大写的形式。Django将会自动地将需要首字母大写的地方大写。
Django提供了用来描述三种数据库关系的方法,分别是:many-to-one
,many-to-many
和one-to-one
。
使用django.db.models.ForeignKey
来定义Many-to-one这种关系。这个类的使用和其他字段的定义一样,也是作为一个属性存在。
ForeignKey
需要一个位置指示参数:当前model相关联的class类名。
例如,Car 和Manufacturer
(制造商)。每个Manufacturer
都会制造很多Car
,但是每辆Car
只属于一家Manufacturer
,这样的关系就称为多对一关系。基于此例子,代码可以编写如下:
from django.db import models
class Manufacture(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer,on_delete=models.CASCADE)
# ...
关于ForeignKey
更加详尽的定义链接如下。
举个例子,每个Pizza
对象都有多个Topping
对象,而多个Topping
对象则可以在多个Pizza
饼上。代码如下:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
————(复杂的多对多情况遇到的时候再补充)—————–
例如,如果你构建了一个名为places
的数据库,你应该在数据库中构建相对标准的东西例如地址,电话号码等。然后,如果你想在places
的基础上创建一个restaurants
的数据表,这时你就可以直接使用places
所定义好的部分,使用的方式就是一种one-to-one
的模式。
如果当前app下models.py
文件中的代码想要调用另外一个app中models.py
中的model,这也是可以的。做法就是在当前文件中以导入类的方式导入你想要使用的外部的model,然后直接使用即可:
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(
ZipCode,
on_delete=models.SET_NULL,
blank=True,
null=True,
)
Django对于字段的限制有两个:
- 字段的名称不能为Python的关键字,这个比较好理解,举例如下:
class Example(models.Model):
class = models.IntegerField() # 'pass' 是Python的保留字符
class Example(models.Model):
foo__bar = models.IntegerField() # 'foo__bar' 有两个下划线
SQL的一些保留字如`join
,where
,select
则是可以在model的字段名称中使用的,因为Django在每次SQL查询中避免了可能发生的冲突。
如果现有的字段无法满足你的需求,你也可以自定义字段。具体的细节参考此链接。
通过使用内部类Meta来设置model的元数据,例子如下:
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
Model的元数据是“任何非字段的数据”,例如ordering的选项,数据表名字(db_table),或者人类可读的单复数名称(verbose_name和verbose_name_plural)。这些都不是Model所必需的,是可选项。
更多关于Meta的选项点击此链接。
objects
:
model最重要的属性是Manager
。它是提供给Django的数据库查询操作的接口,用于从数据库中获取model实例。若非特别声明Manager,它默认的名字为objects
。Manager只能通过model类进行访问,不能通过model实例进行访问。
为model的对象操作定义一般的“row-level”功能。而Manager
方法是对于整张表操作的方法。model的方法应该作用于某一特定的model实例上。
对于使得业务逻辑的统一来说这是一项很有价值的技术。
例如,下面的model有一些常用方法:
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"
@property
def full_name(self):
"Returns the person's full name."
return "%s %s %(self.first_name,self.last_name)
本例中的最后一个方法是一个property
。
model实例有很多方法的接口,你可以通过重写这些方法来实现自己想要的功能:
__str__()
:
Python的”魔力函数“,该函数返回一个表示当前对象的字符串。适用于Python或者Django用于将实例显示为纯字符串的形式,这样的情形往往会出现在交互的命令行窗口或者在admin页面中。
get_absolute_url()
:
该函数告诉Django如何计算一个对象的url。Django
在admin接口中使用该函数,在需要的时候返回对象的url。
还有一些其他方法封装了一些你可能会使用到的数据库操作。尤其是save()
和delete()
方法比较常用。
你可以自由覆写这些方法来获得自己想要的数据库操作。
一个典型的使用情景是如果你想要在保存对象到数据库的时候做一些事情,就可以覆写实现。以save()
函数为例:
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs) # Call the 'real' save() method.
do_something_else()
同样你可以阻止保存行为:
from django.db import models
class Blog(models.Model):
name = models.ChariField(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().save(*args, **kwargs) # Call the 'real' save() method.
不要忘记在覆写的时候调用父类的方法super().save(*args,**kwargs)
,这样可以确保对象可以存储到数据库中。如果你忘记了调用父类的方法,那么所有的操作都不会数据库中生效。
Django提供了一个命令行工具,可以将当前项目下的环境,迁移到当前工作环境下。
在建立了model之后,可以在shell中对model进行操作,执行以下语句,进入shell:
python manage.py shell
执行后,进入python命令行模式,此时就可以在这里对你建立的model进行操作了。
假设我们建立了两个model,定义如下:
class Grades(models.Model):
gname = models.CharField(max_length=20)
gdate = models.DateTimeField()
ggirlnum = models.IntegerField()
gboynum = models.IntegerField()
isDelete = models.BooleanField()
def __str__(self):
return "%s-%d-%d"%(self.gname,self.ggirlnum,self.gboynum)
class Student(models.Model):
sname = models.CharField(max_length=20)
sgender = models.BooleanField()
sage = models.IntegerField()
scontend = models.CharField(max_length=20)
isDelete = models.BooleanField()
sgrade = models.ForeignKey('Grades', on_delete=models.CASCADE)
from myapp.models import *
from django.utils import timezone
from datetime import *
grade1 = Grades() #新建一个数据表实例
grade1.gname = 'table1'
grade1.gboynum = 10
grade1.ggirlnum = 3
grade1.gdate = datetime(year=2018,month=7,day=17)
grade1.isDelete = False
grade1.save()#保存数据表
以上代码执行后,会在mysql数据库的myapp_grades
数据表中插入一条数据。
from myapp.models import *
grade2 = Grades.objects.get(pk=2)
grade2.gboynum = 60
grade2.save()
from myapp.models import *
grade2 = Grades.objects.get(pk=2)
grade2.delete()
from myapp.models import *
grade1 = Grades.objects.get(pk=1)
stu = Student()
stu.sname = "abc"
stu.sage = 20
stu.sgender = False
stu.scontend = "hello abc"
stu.grade = grade1
stu.save()
关联对象下,对于获取关联对象的机集合,有两个主要任务:
获得一条Grades数据所对应的所有学生Student
获得Student所对应的班级Grade
grade1.students_set.all()
运行上面的语句i,可以得到grade1所对应的所有学生的集合。格式可以总结为:
对象名.关联的类名(小写)_set.all()
也可以使用grade1直接创建grade1下的Student对象,代码如下:
grade1.students_set.creat(sname="add",sage=22,sgender=False,scontent="asdfasf")
model方法或者module-level方法的另外一个特性是写传统的SQL语句。
Django中Model的继承方式几乎和Python中类的继承方式几乎一样。所有的model都继承于实例django.db.models.Model
。
你需要做的决定仅仅是你的父类model的角色:是作为一个抽象类,给子类提供一个通用部分的描述?还是直接作为实例,拥有自己的数据表?以下是三种最常用的类继承模式:
Multi-table inheritance
就是这样做的。[Proxy models](https://docs.djangoproject.com/en/2.0/topics/db/models/#proxy-models)
。抽象基类在你想要在你的模型中加入一些信息的时候很有用。在元数据中将基类的参数abstract=True
,这样,该model就不会被用来创建任何数据表。当它被其他model作为基类时,它的字段将会作为继承它基类的字段。子类中的字段名不能和基类中的字段名一样,否则会报错。下面是一个例子:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
Student
模型有三个字段,分别是name
,age
和home_group
。
CommonInfo
模型不能被用作是一个正常的Django模型,因为他是一个抽象基类,它不会生成数据库表或者有manager
,不能被直接实例化或者保存。
当一个抽象基类被创建的时候,Django会声明一些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'
Django对于抽象基类Meta
类做了调整:在使用Meta
属性的时候,会设置abstract=False
。这意味着抽象基类的子类不会自动变成抽象类,除非你自己手动将其设置为True,让其成为抽象类。