django-2-模型

model

一般来说,每一个模型都映射一张数据库表。

  • 每个模型都是一个 Python 的类,这些类继承 django.db.models.Model
  • 模型类的每个属性都相当于一个数据库的字段。

字段

命名限制

  • 定义字段名时应小心避免使用与模型 API冲突的名称, 如 clean, save, or delete 等.
  • 不能为python 关键字
  • 一个字段名称不能包含连续的多个下划线,会影响 Django 查询语法。
  • 字段名不能以下划线结尾,会影响 Django 查询语法。
  • SQL保留字,例如 join, where 或 select, 是 可以被用在模型字段名当中的,因为 Django 在对底层的 SQL 查询当中清洗了所有的数据库表名和字段名,通过使用特定数据库引擎的引用语法。

字段类型

模型中每一个字段都应该是某个 Field 类的实例。字段类型用以指定数据库数据类型。
Django 内置了数十种字段类型;你可以在 模型字段参考 中看到完整列表。如果 Django 内置类型不能满足你的需求,你可以很轻松地编写自定义的字段类型;参见 编写自定义模型字段

常用的几种

BigAutoField & AutoField

一个 IntegerField,根据可用的 ID 自动递增

BigIntegerField

它保证适合从 -9223372036854775808 到 9223372036854775807 的数字。该字段的默认表单部件是一个 NumberInput。

BooleanField

一个 true/false 字段。

该字段的默认表单部件是 CheckboxInput,或者如果 null=True 则是 NullBooleanSelect。

当 Field.default 没有定义时,BooleanField 的默认值是 None。

CharField

一个字符串字段,适用于小到大的字符串。

对于大量的文本,使用 TextField。

DateField

一个日期,在 Python 中用一个 datetime.date 实例表示

默认表单部件是一个 DateInput

选项

auto_now

每次保存对象时,自动将该字段设置为现在。对于“最后修改”的时间戳很有用。

只有在调用 Model.save() 时,该字段才会自动更新。当以其他方式对其他字段进行更新时,如 QuerySet.update(),该字段不会被更新

auto_now_add

当第一次创建对象时,自动将该字段设置为现在。对创建时间戳很有用。请注意,当前日期是 始终 使用的;它不是一个你可以覆盖的默认值。因此,即使你在创建对象时为该字段设置了一个值,它也会被忽略。如果你想修改这个字段,可以设置以下内容来代替 auto_now_add=True :

EmailField

一个 CharField,使用 EmailValidator 来检查该值是否为有效的电子邮件地址。

FileField

一个文件上传字段

选项

primary_key 参数不支持,如果使用,会引起错误。

upload_to

这个属性提供了一种设置上传目录和文件名的方式,可以有两种设置方式。在这两种情况下,值都会传递给 Storage.save() 方法。

如果你指定一个字符串值或一个 Path,它可能包含 strftime() 格式,它将被文件上传的日期/时间所代替(这样上传的文件就不会填满指定的目录)。例如:

class MyModel(models.Model):
    # file will be uploaded to MEDIA_ROOT/uploads
    upload = models.FileField(upload_to="uploads/")
    # or...
    # file will be saved to MEDIA_ROOT/uploads/2015/01/30
    upload = models.FileField(upload_to="uploads/%Y/%m/%d/")

如果你使用的是默认的 FileSystemStorage,这个字符串的值将被附加到你的 MEDIA_ROOT 路径后面,形成本地文件系统中上传文件的存储位置。

storage

一个存储对象,或是一个返回存储对象的可调用对象。它处理你的文件的存储和检索。

FloatField

当 localize 为 False 时是 NumberInput 否则,该字段的默认表单部件是 TextInput。

IntegerField

一个整数。从 -2147483648 到 2147483647 的值在 Django 支持的所有数据库中都是安全的

JSONField

一个用于存储 JSON 编码数据的字段。

TextField

一个大的文本字段。该字段的默认表单部件是一个 Textarea。

URLField

URL 的 CharField,由 URLValidator 验证。

该字段的默认表单部件是一个 URLInput。

on_delete

当一个由 ForeignKey 引用的对象被删除时,Django 将模拟 on_delete 参数所指定的 SQL 约束的行为。

  • CASCADE : 级联删除
  • PROTECT: 防止删除被引用对象。
  • RESTRICT : 通过引发 RestrictedError ( django.db.IntegrityError 的一个子类)来防止删除被引用的对象。与 PROTECT 不同的是,如果被引用的对象也引用了一个在同一操作中被删除的不同对象,但通过 CASCADE 关系,则允许删除被引用的对象。
  • SET_NULL: 设置 ForeignKey 为空;只有当 null 为 True 时,才有可能。
  • SET_DEFAULT 将 ForeignKey 设置为默认值,必须为 ForeignKey 设置一个默认值。
  • SET(): 将 ForeignKey 设置为传递给 SET() 的值,或者如果传递的是可调用对象,则调用它的结果。
  • DO_NOTHING: 不采取任何行动
def get_sentinel_user():
    return get_user_model().objects.get_or_create(username="deleted")[0]


class MyModel(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET(get_sentinel_user),
    )

limit_choices_to

当使用 ModelForm 或管理中渲染该字段时,设置该字段的可用选择限制(默认情况下,查询集中的所有对象都可以选择)。可以使用字典、 Q 对象,或者返回字典或 Q 对象的可调用对象。

related_name

用于从相关对象到这个对象的关系的名称。这也是 related_query_name 的默认值(用于从目标模型反向过滤名称的名称)。

related_query_name

目标模型中反向过滤器的名称。如果设置了,它默认为 related_name 或 default_related_name 的值,否则默认为模型的名称

to_field

关联对象的字段。默认情况下,Django 使用相关对象的主键。如果你引用了一个不同的字段,这个字段必须有 unique=True。

db_constraint

控制是否应该在数据库中为这个外键创建一个约束。

swappable

控制迁移框架的反应,如果这个 ForeignKey 指向一个可交换的模型。

ManyToManyField

一个多对多的关系。

through

Django 会自动生成一个表来管理多对多关系,但是,如果你想手动指定中间表,你可以使用 through 选项来指定代表你要使用的中间表的 Django 模型。

through_fields

只有当指定了一个自定义的中间模型时才会使用,Django 通常会决定使用中介模型的哪些字段来自动建立多对多的关系。

OneToOneField

一对一的关系。类似于 ForeignKey 与 unique=True

parent_link

当 True 并用于从另一个 concrete model 继承的模型中时,表示该字段应被用作回到父类的链接,而不是通常通过子类隐含创建的额外 OneToOneField。

ForeignKey

一个多对一的关系。需要两个位置参数:模型相关的类和 on_delete 选项。

字段选项

null

默认为 False , 相当于数据库 NULL

blank

默认False, 与html 中 input 相关联

choices

存储两个值的元组, 相当于 html 中 select, 元组的第一个值为 index, 第二个值为 value。

数据库中存储的是第一个值。表单中显示的是第二个值。

在 view 中,如果要获取第二个值,可以使用 get_field_display(), 其中field 为具体的字段名。

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)


# ==============================
# view.py
p = Person(name="Fred Flintstone", shirt_size="L")
p.save()
p.get_shirt_size_display()

也可以使用枚举类定义 choices

class Runner(models.Model):
    MedalType = models.TextChoices("MedalType", "GOLD SILVER BRONZE")
    name = models.CharField(max_length=60)
    medal = models.CharField(blank=True, choices=MedalType, max_length=10)

default

该字段的默认值。可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。

db_default

字段的数据库计算的默认值。这可以是一个字面值或一个数据库函数。

如果同时设置了 db_default 和 Field.default,在 Python 代码中创建实例时 default 会优先生效。db_default 仍然会在数据库级别设置,并且在使用 ORM 之外插入行或在迁移中添加新字段时仍然会使用它。

help_text

随表单控件一同显示。

primary_key

bool值,如果为True,则把当前字段设置为主键。

键字段是只可读的,如果你修改一个模型实例的主键并保存,这等同于创建了一个新的模型实例。

默认情况下,Django 给每个模型一个自动递增的主键,其类型在 AppConfig.default_auto_field 中指定,或者在 DEFAULT_AUTO_FIELD 配置中全局指定。

id = models.BigAutoField(primary_key=True)

如果手动设置主键,django将不会自动生成id。

unique

bool, 这个字段唯一

verbose_name

与 html 中 的 label 关联

如果未指定该参数值, Django 会自动使用字段的属性名作为该参数值,并且把下划线转换为空格。

字段之间的关系

多对一 ForeignKey

在表单中呈现方式为select


class Manufacturer(models.Model):
    # ...
    pass


class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

如果一个模型中有多个字段关联到同一个字段,需要使用related_name 加以区分。

class Order(models.Model):
    # 订单创建者,关联到User模型
    creator = models.ForeignKey(
        User, 
        on_delete=models.CASCADE,
        related_name="created_orders"  # 反向引用名称
    )
    
    # 订单接收者,同样关联到User模型
    receiver = models.ForeignKey(
        User, 
        on_delete=models.CASCADE,
        related_name="received_orders"  # 另一个反向引用名称
    )
    

多对多 ManyToManyField

在表单中呈现为多选ckeck

class Topping(models.Model):
    # ...
    pass


class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

使用 through 参数指定多对多关系使用哪个中间模型。

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

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["person", "group"], name="unique_person_group"
            )
        ]

相互访问

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

beatles.members.all()
ringo.group_set.all()

还可以使用 add()、create() 或 set() 来创建关系,只要为任何必需的字段指定 through_defaults

beatles.members.add(john, through_defaults={"date_joined": date(1960, 8, 1)})
beatles.members.create(name="George Harrison", through_defaults={"date_joined": date(1960, 8, 1)} )
beatles.members.set( [john, paul, ringo, george], through_defaults={"date_joined": date(1960, 8, 1)} )

remove

如果由中介模型定义的自定义中介表不对 (model1, model2) 对进行唯一性强制,允许多个值,则 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 deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>

clear

clear() 方法可以用来删除一个实例的所有多对多关系

beatles.members.clear()

一对一 OneToOneField

本质上是一种特殊的 ForeignKey,但会自动添加 unique=True 约束

反向关联时直接返回单个对象(而非查询集)

常用于扩展现有模型(如用户资料扩展)

from django.contrib.auth.models import User

# 示例:为用户扩展详细资料
class UserProfile(models.Model):
    # 与User模型建立一对一关联
    user = models.OneToOneField(
        User,
        on_delete=models.CASCADE,  # 当用户被删除时,关联的资料也会被删除
        related_name='profile'     # 反向引用名称
    )
    phone = models.CharField(max_length=11, blank=True)
    address = models.TextField(blank=True)
    birth_date = models.DateField(null=True, blank=True)
    
    def __str__(self):
        return f"{self.user.username}的资料"

跨文件

1 导入需要被关联的模型
2. lazy reference。

class UserProfile(models.Model):
	 user = models.OneToOneField(
        User,
        on_delete=models.CASCADE,  # 当用户被删除时,关联的资料也会被删除
        related_name='profile'     # 反向引用名称
    )
    # 或者
    user = models.OneToOneField(
        'django.contrib.auth.models.User',
        on_delete=models.CASCADE,  # 当用户被删除时,关联的资料也会被删除
        related_name='profile'     # 反向引用名称
    )

Meta 选项

模型可选参数参考 中列出了 Meta 可使用的全部选项。

abstract

如果 abstract = True,这个模型将是一个抽象基类。

base_manager_name

管理器的属性名,例如,'objects',用于模型的 _base_manager

db_table

用于模型的数据库表的名称, 是用小写,方便和数据库对应。

db_table_comment

用于此模型的数据库表的注释

managed

默认为 True,意味着 Django 会在 migrate 中创建相应的数据库表,或者作为迁移的一部分,并作为 flush 管理命令的一部分删除它们。

如果 False,将不对该模型进行数据库表的创建、修改或删除操作。

ordering

对象的默认排序,用于获取对象列表时

ordering = ["-pub_date", "author"]

indexes
索引

class Customer(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)

    class Meta:
        indexes = [
            models.Index(fields=["last_name", "first_name"]),
            models.Index(fields=["first_name"], name="first_name_idx"),
        ]

constraints

class Customer(models.Model):
    age = models.IntegerField()

    class Meta:
        constraints = [
            models.CheckConstraint(check=models.Q(age__gte=18), name="age_gte_18"),
        ]

verbose_name

verbose_name = "pizza"

verbose_name_plural

verbose_name_plural = "stories"

模型属性

模型当中最重要的属性是 Manager。它是 Django 模型和数据库查询操作之间的接口,并且它被用作从数据库当中 获取实例,如果没有指定自定义的 Manager 默认名称是 objects。Manager 只能通过模型类来访问,不能通过模型实例来访问。

模型方法

__str__()

显示名称

get_absolute_url()

该方法告诉 Django 如何计算一个对象的 URL。Django 在后台接口使用此方法,或任意时间它需要计算一个对象的 URL。

任何需要一个唯一 URL 的对象需要定义此方法。

自定义

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 f"{self.first_name} {self.last_name}"

重写

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

执行自定义SQL

objects.raw()

name_map = {"first": "first_name", "last": "last_name", "bd": "birth_date", "pk": "id"}
Person.objects.raw("SELECT * FROM some_other_table", translations=name_map)

模型继承

Django 有三种可用的集成风格

抽象基类

父类用于子类公共信息的载体

在 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=5)

当一个抽象基类被建立,Django 将所有你在基类中申明的 Meta 内部类以属性的形式提供。若子类未定义自己的 Meta 类,它会继承父类的 Meta。当然,子类也可继承父类的 Meta

注意事项

对 related_name 和 related_query_name 要小心使用,因为是唯一的,如果继承,会导致重复。

为了解决此问题,当你在抽象基类中(也只能是在抽象基类中)使用 related_name 和 related_query_name,部分值需要包含 ‘%(app_label)s’ 和 ‘%(class)s’。

  • ‘%(class)s’ 用使用了该字段的子类的小写类名替换。
  • ‘%(app_label)s’ 用小写的包含子类的应用名替换。每个安装的应用名必须是唯一的,应用内的每个模型类名也必须是唯一的。因此,替换后的名字也是唯一的。
class Base(models.Model):
    m2m = models.ManyToManyField(
        OtherModel,
        related_name="%(app_label)s_%(class)s_related",
        related_query_name="%(app_label)s_%(class)ss",
    )

    class Meta:
        abstract = True


class ChildA(Base):
    pass


class ChildB(Base):
    pass

多表继承

每个模型都是一个单独的模型。每个模型都指向分离的数据表,且可被独立查询和创建。


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)

多表继承情况下,子类不会继承父类的 Meta。所以的 Meta 类选项已被应用至父类,在子类中再次应用会导致行为冲突

代理模型

代理模型就像普通模型一样申明。你需要告诉 Django 这是一个代理模型,通过将 Meta 类的 proxy 属性设置为 True。

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

多重继承

class Article(models.Model):
    article_id = models.AutoField(primary_key=True)
    ...


class Book(models.Model):
    book_id = models.AutoField(primary_key=True)
    ...


class BookReview(Book, Article):
    pass

子类不能和父类的字段名相同

在正常的 Python 类继承中,允许子类覆盖父类的任何属性。在 Django 中,模型字段通常不允许这样做。如果一个非抽象模型基类有一个名为 author 的字段,你就不能在继承自该基类的任何类中,创建另一个名为 author 的模型字段或属性。(抽象基类除外)

在一个包中管理模型

如果有很多 models.py 文件,用独立的文件管理它们会很实用。

为了达到此目的,创建一个 models 包。删除 models.py,创建一个 myapp/models 目录,包含一个 init.py 文件和存储模型的文件。你必须在 init.py 文件中导入这些模块。

# __init__.py
from .organic import Person
from .synthetic import Robot
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值