python3之Django模型(一)

1、模型概述

模型是关于您的数据的唯一,明确的信息来源,它包含您正在存储的数据的重要字段和行为。通常,每个模型映射到单个数据库表。

每个模型都是一个子类的python类django.db.models.Model

模型的每个属性表示一个数据字段

综上所述,Django为您提供了一个自动生成的数据库访问API。

简单实例:在app下的models中创建person类:

class person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

first_name和last_name是模型的命名字段,每个字段都被指定为一个类属性,并且每个属性映射到一个数据表的列上,上面的person模型会创建一个如下的SQL数据库表语句:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

其中表的名称以APP名为表的前缀命名myapp_person,Django自动从模型类的名称和包含它的应用程序中派生出数据库表的名称,模型的数据库表格名称是通过将模型的“应用标签”(您使用的名称)与模型的类名称加在一起并在它们之间加下划线来构造的。要修改它请使用模型meta元数据选项db_table来指定表名

模型会自动添加一个自增长的主键字段

使用模型:要使用模型,需要在项目的setting.py文件中修改INSTALLED_APPS设置,来添加应用程序名,然后迁移数据。

INSTALLED_APPS = [
    'myapp',
]

#python manage.py makemigrations
#python manage.py migrate

2、字段(字段类型与字段选项)

模型中最重要的部分,字段在python中表现为一个类属性,体现了数据库中的一个列

在命名时请避免使用python关键字,django内置模型API名字,防止命名冲突;字段名中不能有两个以上下划线,因为两个下划线是Django的查询语法

(1)字段类型:

模型中的每个字段都是相应Field类的实例,它们都位于django.db.models中,Django使用字段类型来确定列类型,呈现表单字段时使用默认的HTML,Django管理员和自动生成的表单中使用的最小验证要求。

常用的Django字段类型:

AutoField()  :一个自动增加的整数类型字段。通常你不需要自己编写它,Django会自动帮你添加字段:id = models.AutoField(primary_key=True),这是一个自增字段,从1开始计数。如果你非要自己设置主键,那么请务必将字段设置为primary_key=True。Django在一个模型中只允许有一个自增字段,并且该字段必须为主键

BigAutoField():和AutoField差不多,不同之处在于它是64位整数类型自增字段,数字范围更大,从1到9223372036854775807

BigIntegerField:64位整数字段(看清楚,非自增),类似IntegerField ,-9223372036854775808 到9223372036854775807。在Django的模板表单里体现为一个textinput标签

BinaryField:二进制数据类型。使用受限,少用

BooleanField:布尔值类型。默认值是None。在HTML表单中体现为CheckboxInput标签。如果要接收null值,请使用NullBooleanField

CharField:字符串类型。必须接收一个max_length参数,表示字符串长度不能超过该值。默认的表单标签是input text。最常用的filed

CommaSeparatedIntegerField:逗号分隔的整数类型。必须接收一个max_length参数。常用于表示较大的金额数目,例如1,000,000元

DateField:class DateField(auto_now=False, auto_now_add=False, **options)日期类型。一个Python中的datetime.date的实例。在HTML中表现为TextInput标签。在admin后台中,Django会帮你自动添加一个JS的日历表和一个“Today”快捷方式,以及附加的日期合法性验证。两个重要参数:(参数互斥,不能共存) auto_now:每当对象被保存时将字段设为当前日期,常用于保存最后修改时间。auto_now_add:每当对象被创建时,设为当前日期,常用于保存创建日期(注意,它是不可修改的)。设置上面两个参数就相当于给field添加了editable=Falseblank=True属性。如果想具有修改属性,请用default参数。例子:pub_time = models.DateField(auto_now_add=True),自动添加发布时间。

DateTimeField:日期时间类型。Python的datetime.datetime的实例。与DateField相比就是多了小时、分和秒的显示,其它功能、参数、用法、默认值等等都一样

DecimalField:固定精度的十进制小数。相当于Python的Decimal实例,必须提供两个指定的参数!参数max_digits:最大的位数,必须大于或等于小数点位数 。decimal_places:小数点位数,精度。 当localize=False时,它在HTML表现为NumberInput标签,否则是text类型。例子:储存最大不超过999,带有2位小数位精度的数,定义如下:models.DecimalField(..., max_digits=5, decimal_places=2)

DurationField:持续时间类型。存储一定期间的时间长度。类似Python中的timedelta。在不同的数据库实现中有不同的表示方法。常用于进行时间之间的加减运算。但是小心了,这里有坑,PostgreSQL等数据库之间有兼容性问题

EmailField:邮箱类型,默认max_length最大长度254位。使用这个字段的好处是,可以使用DJango内置的EmailValidator进行邮箱地址合法性验证

FileField:class FileField(upload_to=None, max_length=100, **options)上传文件类型,它不能设置为主键,默认情况下,该字段在HTML中表现为一个ClearableFileInput标签,在数据库内,我们实际保存的是一个字符串类型,默认最大长度100,可用通过max_length参数自定义,真实的文件是保存在服务器的文件系统内的,最重要的参数upload_to用于设置上传地址的目录和文件名,如下示例:

class MyModel(models.Model):
    # 文件被传至`MEDIA_ROOT/uploads`目录,MEDIA_ROOT由你在settings文件中设置
    upload = models.FileField(upload_to='uploads/')
    # 或者
    # 被传到`MEDIA_ROOT/uploads/2015/01/30`目录,增加了一个时间划分
    upload = models.FileField(upload_to='uploads/%Y/%m/%d/')

FilePathField:文件路径类型

class FilePathField(path=None, match=None, recursive=False, max_length=100, **options)[source]

一种用来保存文件路径信息的字段,在数据表内以字符串的形式存在,默认最大长度100,可以通过max_length参数设置,它包含有以下参数:

path:必须指定的参数,表示一个系统绝对路径

match:可选参数,一个正则表达式,用于过滤文件名,只匹配基本文件名,不匹配路径

recursive:可选参数,只能是True或者False,默认为False,决定是否包含子目录,也就是是否递归的意思

allow_files:可选参数,只能是True或者False,默认为True,决定是否应该将文件名包括在内,它和allow_folders其中,必须有一个为True

allow_folders:可选参数,只能是True或者False,默认为False,决定是否应该将目录名包括在内

 

FloatField:浮点数类型,参考整数类型

ImageFiedl:图像类型;用于保存图像文件的字段。其基本用法和特性与FileField一样,只不过多了两个属性height和width。默认情况下,该字段在HTML中表现为一个ClearableFileInput标签。在数据库内,我们实际保存的是一个字符串类型,默认最大长度100,可以通过max_length参数自定义。真实的图片是保存在服务器的文件系统内的。

height_field参数:保存有图片高度信息的模型字段名。 width_field参数:保存有图片宽度信息的模型字段名。

使用Django的ImageField需要提前安装pillow模块,pip install pillow即可

使用FileField或者ImageField字段的步骤:

(1)在settings文件中,配置MEDIA_ROOT作为上传文件在服务器中的基础路径,为了性能考虑,这些文件不会被存储在数据库中,在配置个MEDIA_URL作为公用URL,指向上传文件的基本路径,请确保Web服务器的用户账号对该目录具有写入权限

(2)添加FileField或者ImageField字段到模型中,定义好upload_to参数,文件最总会放在MEDIA_ROOT目录的'upload_to‘子目录中

(3)所有真正被保存在数据库中的只是指向上传文件路径的字符串而已,可用通过url属性,在Djang的模块中方便的访问这些文件;列如:一个ImageField字段名为mug_shot,那么在Django模块的HTML文件中,可用使用{{ object.mug_shot.url }}来获取该文件,其中的object用具体的对象名称代替

(4)可以通过name和size属性,获取文件的名称和大小信息

 

IntegerField:整数类型,最常用的字段之一,取值范围-2147483648到2147483647。在HTML中表现为NumberInput标签。

GenericIPAddressField:class GenericIPAddressField(protocol='both', unpack_ipv4=False, **options)[source],IPV4或者IPV6地址,字符串形式,例如192.0.2.30或者2a02:42fe::4在HTML中表现为TextInput标签。参数protocol默认值为‘both’,可选‘IPv4’或者‘IPv6’,表示你的IP地址类型

NullBooleanField:类似布尔字段,只不过额外允许NULL作为选项之一

PositiveIntegerField:正整数字段,包含0,最大2147483647

PositiveSmallIntegerField:较小的正整数字段,从0到32767

SlugField:slug是一个新闻行业的术语。一个slug就是一个某种东西的简短标签,包含字母、数字、下划线或者连接线,通常用于URLs中。可以设置max_length参数,默认为50

SmallIntegerField:小整数,包含-32768到32767

TextField:大量文本内容,在HTML中表现为Textarea标签,最常用的字段类型之一!如果你为它设置一个max_length参数,那么在前端页面中会受到输入字符数量限制,然而在模型和数据库层面却不受影响。只有CharField才能同时作用于两者

TimeField:时间字段,Python中datetime.time的实例。接收同DateField一样的参数,只作用于小时、分和秒

URLField:一个用于保存URL地址的字符串类型,默认最大长度200

UUIDField:用于保存通用唯一识别码(Universally Unique Identifier)的字段。使用Python的UUID类。在PostgreSQL数据库中保存为uuid类型,其它数据库中为char(32)。这个字段是自增主键的最佳替代品

数据库无法自己生成uuid,因此需要如下使用default参数:

import uuid     # Python的内置模块
from django.db import models

class MyUUIDModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # 其它字段

(2)字段参数

每个字段都有一组特定于字段的参数,下面列出通用参数为可选的:

null:如果True,Django将像NULL数据库中那样存储空值默认是False

blank:如果True,该字段被允许为空;默认是False。和Null参数不同的是,null是纯数据库层面的,而blank是验证相关的,它与表单验证是否允许输入框内为空有关,与数据库无关,所以要小心一个null为False,blank为True的字段接收到一个空值可能会出bug或异常

choices:一个二维元组可迭代的对象(例如,一个列表或元组),用作该字段的选择,用于页面上的选择框标签,第一个元素表示存在数据库内真实的值,第二个元素表示页面上显示的具体内容,在浏览器页面上将显示第二个元素的值:

YEAR_IN_SCHOOL_CHOICES = (
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
)

db_column:该参数用于定义当前字段在数据表内的列名,如果未指定,Django将使用字段名作为列名

db_index:该参数接收布尔值,如果为True,数据库将为该字段创建索引

db_tablespace:用于字段索引的数据库表空间的名字,前提是当前字段设置了索引,默认值为工程的DEFAULT_INDEX_TABLESPACE设置,如果使用的数据库不支持表空间,该参数会被忽略

default:字段的默认值。这可以是一个值或一个可调用的对象。如果可调用,则每次创建新对象时都会调用它。设置的默认值不能是一个可变对象,比如列表,集合等,lambda匿名函数也不可用于default的调用对象,因为匿名函数不能被migrations序列化,注意:在某种原因不明的情况下将default设置为None,可能引发intergyerror:not null constraint failed,即非空约束失败异常,导致python manage.py migrate失败,此时可将None改为Fasle或其他值,只要不是None就行。

editable:如果设为false那么当前字段将不会在admin后台或者其他的ModelForm表单中显示,同时还会被模型验证功能跳过,参数默认值为True

error_messages:用于自定义错误信息,参数接收字典类型的值,字典的键可以是null,blank,invalid,invalid_choice,unique和unique_for_date其中的一个。

help_text:用窗体小部件显示额外的“帮助”文本。即使您的字段未用于表单,对于文档也很有用。

primary_key:如果True,这个字段是模型的主键。如果您没有primary_key=True为模型中的任何字段指定,Django会自动添加一个 AutoField自增字段,名为'id',并设置为主键,也就是id = models.AutoField(primary_key=True),如果你为某个字段设置了primary_key=True,则当前字段变为主键,并关闭Django自动生成id主键的功能;primary_key=True隐含null=False和unique=True的意思,一个模型中只能由一个主键字段,另外主键字段不可修改,如果给某个对象的主键赋个新值实际上是创建一个新对象,并不会修改原来的对象

unique:如果为True,该字段在整个表格中必须是唯一的,注意:对于ManyToManyField和OneToOneField关系类型,该参数无效;当unique=True时,db_index参数无需设置,因为unqiue隐含了索引

unique_for_date:日期唯一,如果由一个title字段,并设置了参数unique_for_date="pub_date",那么django将不允许由两个模型对象具备同样的title和pub_date,有点类似联合约束。

unique_for_month:月份唯一

unique_for_year:年份唯一

verbose_name:为字段设置一个人类可读的,更加直观的别名,对于每个字段类型,除了ForeignKey、ManyToManyField和OneToOneField这三个特殊的关系类型,其第一可选位置参数都是verbose_name,如果没指定这个参数,Django会利用字段的属性名自动创建它,并将下划线转换为空格

#下面列子的verbose_naem是“person'sfirst name"
first_name = models.CharField("person's first name", max_length=30)
#下面例子的verbose_name是”first name"
first_name = models.CharField(max_length=30)
#由于外键、多对多和一对一字段,第一个参数需要用来指定关联的模型,因此必须用关键参数verbose_name来明确指定
poll = models.ForeignKey(
    Poll,
    on_delete=models.CASCADE,
    verbose_name="the related poll",
    )
#另外,你无须大写verbose_name的首字母,Djang自动为你完成这项工作

validators:运行在该字段上的验证器的列表

3、自动主键字段

默认情况下,Django为每个模型提供以下字段:

id = models.AutoField(primary_key=True)

这是一个自动递增的主键;如果你想指定一个自定义主键,只需primary_key=True在其中一个字段上指定 。如果Django看到你已经明确设置Field.primary_key,它不会添加自动 id列。

每个模型只需要一个字段primary_key=True(显式声明或自动添加)

4、详细的字段名

每个字段类型除了ForeignKey, ManyToManyField和 OneToOneField都有一个可选的第一个位置参数 - 一个详细名称。如果没有给出详细名称,Django将使用该字段的属性名称自动创建它,并将下划线转换为空格。

first_name = models.CharField("person's first name", max_length=30)

上面的例子中详细名称为:“person's first name”

first_name = models.CharField(max_length=30)

如果第一个位置参数不制定详细名则使用字段属性名来创建它,上面的例子详细名为“first name"

还可用在字段中使用verbose_name选项来指定字段名

 

5、关系型数据库

关系数据库的强大之处在于相互关联表。Django提供了定义三种最常见类型的数据库关系的方法:多对一,多对多和一对一。

(1)多对一的关系

要定义多对一关系,请使用django.db.models.ForeignKey您可以像使用其他Field类型一样使用它,多对一的关系通常被称为外键,外键字段类的定义如下:

class ForeignKey(to,no_delete,**options)

多对一的关系,需要两个位置参数,第一个为关联的模型,另一个是no_delete选项models.ForeignKey('self',no_delete=models.CASCADE) :

当一张表中创建一行数据时,有一个单选的下拉框且可用被重复选择

ForeignKey(ForeignObject) # ForeignObject(RelatedField)
        to,                         # 要进行关联的表名
        to_field=None,              # 要关联的表中的字段名称
        on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为
         related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
        related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
        limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
                                    # 如:
                                            - limit_choices_to={'nid__gt': 5}
                                            - limit_choices_to=lambda : {'nid__gt': 5}

                                            from django.db.models import Q
                                            - limit_choices_to=Q(nid__gt=10)
                                            - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
                                            - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
        db_constraint=True          # 是否在数据库中创建外键约束
        parent_link=False           # 在Admin中是否显示关联数据

例如以下实例:在一篇博客文章中引用多个用户可选择。

#下面以创建一个对应多用户的博客文章模型为例来说明:

from django.db import models
from django.utils import timezone

class User(models.Model):
    username = models.CharField(max_length=150,verbose_name="用户名")
    password = models.CharField(max_length=300,verbose_name="密码")
    age = models.IntegerField(verbose_name="年龄")
    def __str__(self):
        return self.username

class articel(models.Model):
    title = models.CharField(max_length=300,verbose_name="博客标题")
    author = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name="作者")   #作者将对应User表中的多个用户,提供下拉菜单可选择用户
    body = models.TextField(verbose_name="文章内容")
    publish = models.DateTimeField(default=timezone.now,verbose_name="发表时间")

    class Meta:
        ordering = ["-publish"]

    def __str__(self):
        return self.title

如果要关联的模型对象在另外一个app中,假设user模型在blog这个app中,要在当前app模型中关联,如下设置:

class articel(models.Model):
    author = models.ForeignKey(
        'blog.user',    #app之间关联
        on_delete=models.CASCADE,
)

如果要创建一个递归的外键,也就是自己关联自己的外键,使用下面的方法:

models.ForeignKey('self',on_delete=models.CASCADE)

外键还有一些重要的参数:on_delete当一个被外键关联的对象被删除时,Django将模仿on_delete参数定义的SQL约束执行相应的操作,比如有一个可为空的外键,并且你想让它在关联的对象被删除时,自动设为null,可如下定义:

user = models.ForeignKey(
    User,
    models.SET_NULL,
    blank=True,
    null=True,
)

该参数可选的值都内置在django.db.models中,包括:

CASCADE:模拟SQL语言中的ON DELETE CASCADE约束,将定义有外键的模型对象同时删除,该操作为当前django版本的默认操作

PROTECT:阻止上面的删除操作,但是弹出ProtectedError异常

SET_NULL:将外键字段设为Null,只用当字段设置了Null=True时,方可使用该值

SET_DEFAULT:将外键字段设为默认值。只有当字段设置了default参数时,方可使用

DO_NOTHING:什么也不做

SET():设置为一个传递给SET()的值或者一个回调函数的返回值;注意大小写

(2)多对多关系

要定义多对多关系,请使用 ManyToManyField您可以像使用其他Field类型一样使用它 :将其作为模型的类属性包含在内。

ManyToManyField 需要一个位置参数:与模型相关的类。

ManyToManyField(RelatedField)
        to,                         # 要进行关联的表名
        related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
        related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
        limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
                                    # 如:
                                            - limit_choices_to={'nid__gt': 5}
                                            - limit_choices_to=lambda : {'nid__gt': 5}

                                            from django.db.models import Q
                                            - limit_choices_to=Q(nid__gt=10)
                                            - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
                                            - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
        symmetrical=None,           # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
                                    # 做如下操作时,不同的symmetrical会有不同的可选字段
                                        models.BB.objects.filter(...)

                                        # 可选字段有:code, id, m1
                                            class BB(models.Model):

                                            code = models.CharField(max_length=12)
                                            m1 = models.ManyToManyField('self',symmetrical=True)

                                        # 可选字段有: bb, code, id, m1
                                            class BB(models.Model):

                                            code = models.CharField(max_length=12)
                                            m1 = models.ManyToManyField('self',symmetrical=False)

        through=None,               # 自定义第三张表时,使用字段用于指定关系表
        through_fields=None,        # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
                                        from django.db import models

                                        class Person(models.Model):
                                            name = models.CharField(max_length=50)

                                        class Group(models.Model):
                                            name = models.CharField(max_length=128)
                                            members = models.ManyToManyField(
                                                Person,
                                                through='Membership',
                                                through_fields=('group', 'person'),
                                            )

                                        class Membership(models.Model):
                                            group = models.ForeignKey(Group, on_delete=models.CASCADE)
                                            person = models.ForeignKey(Person, on_delete=models.CASCADE)
                                            inviter = models.ForeignKey(
                                                Person,
                                                on_delete=models.CASCADE,
                                                related_name="membership_invites",
                                            )
                                            invite_reason = models.CharField(max_length=64)
        db_constraint=True,         # 是否在数据库中创建外键约束
        db_table=None,              # 默认创建第三张表时,数据库中表的名称

 #以上面的例子为例再增加一个评论的类模型,并关联到博客表中,它是一个可用多选的下拉框。

class comment(models.Model):
    user = models.ForeignKey(User,on_delete=models.CASCADE)
    date = models.DateTimeField(default=timezone.now)
    body = models.TextField()

class articel(models.Model):
    title = models.CharField(max_length=300,verbose_name="博客标题")
    author = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name="作者")
    body = models.TextField(verbose_name="文章内容")
    publish = models.DateTimeField(default=timezone.now,verbose_name="发表时间")
    comment = models.ManyToManyField(comment,verbose_name="评论")  #在每一篇文章中针对多个评论,即为多对多的关系
    class Meta:
        ordering = ["-publish"]

    def __str__(self):
        return self.title

对于ManyToManyField字段,Django采用的是第三张中间表的方式,通过这第三张表,来关联ManyToMany的双方,下面通过具体的列子来详细解析:

首先建立一个简单的多对多关系模型:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)
    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person)
    def __str__(self):
        return self.name

在Group模型中,通过members字段,以ManyToMany方式与Person模型建立多对多关系,我们在数据库中可以看到三张表,以app名为前缀,如app01则三张表名为:app01_group、app01_person、app01_group_members

其中app01_group_memebers是对应关系表,它会生成自身id列,group的id列和person相对应的id列,此中间表是通过id的关联进行映射的。

自定义中间表:

一般情况,普通的多对多已经够用,无需自己创建第三张关系表,但是某些情况可能更复杂一点,比如如果你想保存某个人加入某个分组的时间呢?想保存进组的原因呢?

Django提供了一个through参数,用于指定中间模型,你可以将类似进组时间,邀请原因等其他字段都放在这个中间模型内:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)
    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person,through='Membership')  #指定中间表
    def __str__(self):
        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=100)

(3)一对一关系

要定义一对一的关系,请使用 OneToOneField您可以像使用其他Field类型一样使用它 :将其作为模型的类属性包含在内。

当对象以某种方式“扩展”另一个对象时,这对于对象的主键非常有用。

OneToOneField 需要一个位置参数:与模型相关的类。

OneToOneField(ForeignKey)
        to,                         # 要进行关联的表名
        to_field=None               # 要关联的表中的字段名称
        on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为

                                    ###### 对于一对一 ######
                                    # 1. 一对一其实就是 一对多 + 唯一索引
                                    # 2.当两个类之间有继承关系时,默认会创建一个一对一字段
                                    # 如下会在A表中额外增加一个c_ptr_id列且唯一:
                                            class C(models.Model):
                                                nid = models.AutoField(primary_key=True)
                                                part = models.CharField(max_length=12)

                                            class A(C):
                                                id = models.AutoField(primary_key=True)
                                                code = models.CharField(max_length=1)

一对一:在创建表中一行数据时,有一个单选的下拉框,下拉框中的内容被用过一次就消失了。

class User(models.Model):
    username = models.CharField(max_length=150,verbose_name="用户名")
    password = models.CharField(max_length=300,verbose_name="密码")
    age = models.IntegerField(verbose_name="年龄")

    def __str__(self):
        return self.username

class User_extend(models.Model):
    User = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)  #将用户信息扩展到此表
    address = models.CharField(max_length=300,null=True,blank=True,verbose_name="住址")
    education = models.CharField(max_length=30,null=True,blank=True,verbose_name="学历")

6、跨文件的模型

将模型与另一个应用程序中的模型相关联完全可以。为此,请在定义模型的文件顶部导入相关模型。然后,只需在需要的地方引用其他模型类。

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,
    )

7、字段名称限制

Django对模型字段名称只有两个限制:

(1)字段名称不能是Python保留字,因为这会导致Python语法错误

(2)由于Django查询查找语法的工作方式,字段名称不能在一行中包含多个下划线

8、Meta选项

模型源数据Meta是数据库类可选的附加属性,如排序(ordering),数据库表名(db_table),可读的数据类名称(verbose_name)

下面列出可用的Meta选项:

abstract = True   :定义模型为抽象基类,抽象模型本身不实际生成数据表,而是作为其他模型的父类,被继承使用

app_label = 'myapp'  :声明模型属于哪个应用,如定义了模型的app没有在INSTALLED_APPS中注册,则必须通过此元选项声明它属于哪个app

base_manager_name   :用于模型的管理器名称

db_table = ‘db_name'  :指定模型使用的数据库名称

db_tablespace:自定义数据库表空间的名称,默认值是工程DEFAULT_TABLESPACE设置

default_manager_name:自定义模型的_default_manager管理器的名字

ordering = ['order_date']  :指定对象的默认排序,可对指定排序规则

verbose_name = “blog”   :指定对象的可读名称

verbose_name_plural:指定模型对象的复数名,在使用中文时,如果不指定该参数,那么默认的复数名是verbose_name加上‘s'

label:前面介绍的元数据都是可修改和设置的,但还有两个只读的元数据,label就是其中之一,app_label.object_name,例如'polls.Question'

class blog(models.Model):
    pass

    class Meta:
        ordering = ['-publish']
        verbose_name = "博客"

9、模型属性

模型的最重要的属性是 Manager它是通过它向Django模型提供数据库查询操作的接口,用于 从数据库中检索实例如果Manager未定义定义,则默认名称为 objects管理者只能通过模型​​类访问,而不能访问模型实例。

from blog.models import User_extend
date = User_extend.objects.all()

10、模型继承

Django中的模型继承以与Python中的普通类继承完全不同的方式完成,但基本思想在一开始就很常见。那就是它的基类django.db.models.Model必须是它的子类。

 django中有三种可能的继承方法:

1.定义一个抽象基类,它不单独使用;用来给子类继承使用

2.如果要创建现有模型的子类,并且希望在数据库中为每个模型定义一个表,那么最好使用多个表的继承

3.如果要在有python级别修改模型的行为而不以任何方式修改模型的字段,则可用使用代理模型

(1)抽象基类

当想要将一些公共信息放入许多其他模型时,抽象基类会派上用场,在数据模型的Meta类中定义abstract=True,则此模型不会创建任何数据库表,当它用作其他模型的基类时,其字段将添加到子类的字段中。

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=30)

#该Student模型将会继承name和age字段和自身的home_group组成,该CommonInfo模型不能用作普通的django模型,它是一个抽象类,它不生成数据库表,并且无法直接实例化或保存,从抽象类继承的字段可用使用其他字段或值覆盖,也可用使用删除None

Meta继承:

当创建抽象基类时,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'

#Student子类会继承父类中的Meta内部的属性

 11、多表继承

多表继承中父类和子类都是独立自主功能完整,可正常使用的模型,都由自己的数据库表,内部隐含了一个一对一的关系,如下:

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)
    def __str__(self):
        return "{}-{}".format(self.name,self.address)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)
    def __str__(self):
        return "{}-{}-{}-{}".format(self.name,self.address,self.serves_hot_dogs,self.serves_pizza)
#Restaurant继承Place后会有name和address字段,它会在内部字段中创建一个一对一的主键关系来将父类中的字段数据映射到自身数据表上
  operations = [
        migrations.CreateModel(
            name='Place',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=50)),
                ('address', models.CharField(max_length=80)),
            ],
        ),
        migrations.CreateModel(
            name='Restaurant',
            fields=[
                ('place_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='app01.Place')),
                ('serves_hot_dogs', models.BooleanField(default=False)),
                ('serves_pizza', models.BooleanField(default=False)),
            ],
            bases=('app01.place',),
        ),
    ]

如在Restaurant数据表中写入数据,而继承字段数据实际是写入父类数据表中的,它的内部机制实际隐含一个OneToOne字段,并设置parent_link=True来映射表字段,我们也可以通过这种方法来自定义

Meta和多表继承:

在多表继承的情况下,由于父类和子类都在数据库内有物理存在的表,父类的Meta类会对子类造成不确定的影响,因此,Django在这种情况下关闭了子类继承父类的Meta功能,这点和抽象类的继承有所不同

但是,还有两个Meta元数据库特殊点,那就是ordering和get_latest_by,这两个参数是会被继承的,因此如果在多变继承中,你不想让你的子类继承父类的撒谎给你吗两个参数,就必须在子类中重写

class ChildModel(ParentModel):
  ...
  class Meta:
    ordering = []  #删除父类的排序影响

 

posted @ 2019-02-27 17:28  Py.qi  阅读(1823)  评论(0编辑  收藏  举报