Django models

448 阅读10分钟

docs.djangoproject.com/zh-hans/3.2…

字段通用参数

1.null

  • null : 如果设置为 True,当该字段为空时,Django 会将数据库中该字段设置为 NULL。默认为 False 。

2.blank

  • blank:如果设置为 True,该字段允许为空。默认为 False。注意该选项与 null 不同, null 选项仅仅是数据库层面的设置,而 blank 是涉及表单验证方面。如果一个字段设置为 blank=True ,在进行表单验证时,接收的数据该字段值允许为空,而设置为 blank=False 时,不允许为空。

2.5 null,blank,default的区别

stackoverflow.com/questions/4…

  • null=True的时候,Django会将数据库的所有空值存为Null,如果没有设置null=True时,空的字符串总是会存为"",而不是Null。所以通常情况,我们只对非字符串字段使用null=True,如integers, booleans and dates.对于这几种类型的字段,如果你希望在表单中允许空值,你还需要设置 blank=True,因为null参数只影响数据库存储(见 blank)。
  • blank=True的时候,允许该字段不填。注意,这与null不同。null纯粹与数据库有关,而blank则与验证有关。如果一个字段的空白=True,Django管理网站的验证将允许输入一个空值。如果一个字段的 blank=False,该字段将是必填的。
  • default=...,该字段的默认值。

我的总结:default和null是同一类东西,它们是面向数据库存储的,就是当没有填该字段时,就用默认值default=...或者用None(null=True)的情况,我感觉default的功能覆盖了null,但是应该是因为default不能设置一个None的默认值,所以需要一个null来分配一个None的默认值

3.choices

  • choices:一系列二元组,用作此字段的选项。如果提供了二元组,默认表单小部件是一个选择框,而不是标准文本字段,并将限制给出的选项。

一个选项列表:

YEAR_IN_SCHOOL_CHOICES = [
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
]

每当 choices 的顺序变动时将会创建新的迁移。

每个二元组的第一个值会储存在数据库中,而第二个值将只会用于在表单中显示。

对于一个模型实例,要获取该字段二元组中相对应的第二个值,使用 get_FOO_display() 方法。(其中,这个FOO表示字段名) 例如:

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'

4.default

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

5.help_text

  • help_text:额外的“帮助”文本,随表单控件一同显示。即便你的字段未用于表单,它对于生成文档也是很有用的。

6.primary_key

  • primary_key:如果设置为 True ,将该字段设置为该模型的主键。在一个模型中,如果你没有对任何一个字段设置 primary_key=True 选项。 Django 会自动添加一个 IntegerField 字段,并设置为主键,因此除非你想重写 Django 默认的主键设置行为,你可以不手动设置主键。 主键字段是只可读的,如果你修改一个模型实例的主键并保存,这等同于创建了一个新的模型实例。 例如:
from django.db import models

class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)
    # name是一个主键
>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
<QuerySet ['Apple', 'Pear']>

可以看到,当我们执行fruit.name='Pear'时,由于是主键,所以行为是创建一个新的元组.

7.unique

*unique:如果设置为 True,这个字段的值必须在整个表中保持唯一。

更多的默认参数信息见下面的链接. docs.djangoproject.com/zh-hans/3.2…

关联关系

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

1.多对一关联(ForeignKey)

定义一个多对一的关联关系,使用 django.db.models.ForeignKey 类。就和其它 Field 字段类型一样,只需要在你模型中添加一个值为该类的属性。

ForeignKey类需要添加一个位置参数,即你想要关联的模型类名。

例如,如果一个 Car 模型有一个制造者 Manufacturer --就是说一个 Manufacturer 制造许多辆车,但是每辆车都仅有一个制造者-- 那么使用下面的方法定义这个关系:

from django.db import models

class Manufacturer(models.Model):#制造者
    # ...
    pass

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

2.多对多关联(ManytoManyField)

定义一个多对多的关联关系,使用 django.db.models.ManyToManyField 类。就和其他 Field 字段类型一样,只需要在你模型中添加一个值为该类的属性。

ManyToManyField 类需要添加一个位置参数,即你想要关联的模型类名。

例如:如果 Pizza 含有多种 Topping (配料) -- 也就是一种 Topping 可能存在于多个 Pizza 中,并且每个 Pizza 含有多种 Topping --那么可以这样表示这种关系:

from django.db import models

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

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

对于多对多关联关系的两个模型,可以在任何一个模型中添加 ManyToManyField 字段,但只能选择一个模型设置该字段,即不能同时在两模型中添加该字段。

一般来讲,应该把 ManyToManyField 实例放到需要在表单中被编辑的对象中。在之前的例子中, toppings 被放在 Pizza 当中(而不是 Topping 中有指向 pizzas 的 ManyToManyField 实例 )因为相较于配料被放在不同的披萨当中,披萨当中有很多种配料更加符合常理。按照先前说的,在编辑 Pizza 的表单时用户可以选择多种配料。

3.在多对多(many-to-many)关系中添加添加额外的属性字段

如果你只是想要一个类似于记录披萨和配料之间混合和搭配的多对多关系,标准的 ManyToManyField 就足够你用了。但是,有时你可能需要将数据与两个模型之间的关系相关联。

举例来讲,考虑一个需要跟踪音乐人属于哪个音乐组的应用程序。在人和他们所在的组之间有一个多对多关系,你可以使用 ManyToManyField 来代表这个关系。然而,你想要记录更多的信息在这样的关联关系当中,比如你想要记录某人是何时加入一个组的

对于这些情况,Django 允许你指定用于控制多对多关系的模型。你可以在中间模型当中添加额外的字段。在实例化 ManyToManyField 的时候使用 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=64)

你需要在设置中间模型的时候,显式地为多对多关系中涉及的中间模型指定外键。这种显式声明定义了这两个模型之间是如何关联的。

在中间模型当中有一些限制条件:

  • 你的中间模型要么有且 仅 有一个指向源模型(我们例子当中的 Group )的外键要么你必须通过 ManyToManyField.through_fields 参数在多个外键当中手动选择一个外键,如果有多个外健且没有用 through_fields 参数选择一个的话,会出现验证错误。对于指向目标模型(我们例子当中的 Person )的外键也有同样的限制。
  • 在一个用于描述模型当中自己指向自己的多对多关系的中间模型当中,可以有两个指向同一个模型的外健,但这两个外健分表代表多对多关系(不同)的两端。如果外健的个数 超过 两个,你必须和上面一样指定 through_fields 参数,要不然会出现验证错误。

现在你已经通过中间模型完成你的 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()
<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>]>

可以看到,line4中,我们将person=一个Person对象,group=一个Group对象,data_joined=data(1962,8,16),将invite_reason写入一个字符串,就完成了这个关联类的写入.然后m1.save()

...后面先不看了,有空再去看文档

4.一对一模型

Meta选项

image.png

模型方法

docs.djangoproject.com/zh-hans/3.2…

1.get() 查找对象

返回与给定的查找参数相匹配的对象,其格式应该在 Field lookups 中描述。你应该使用保证唯一的查询比如主键或唯一约束中的字段。例如:

Entry.objects.get(id=1)
Entry.objects.get(blog=blog, entry_number=1)
  • 如果 get() 没有找到任何对象,它会引发一个 表名.DoesNotExist 异常

  • 如果 get() 发现多个对象,会引发一个 表名.MultipleObjectsReturned 异常 举一个例子:

from django.core.exceptions import ObjectDoesNotExist

try:
    blog = Blog.objects.get(id=1)
    entry = Entry.objects.get(blog=blog, entry_number=1)
except ObjectDoesNotExist:
    print("Either the blog or entry doesn't exist.")

2.create() 创建对象

一种方便的方法,用于创建一个对象并一步到位地保存.因此:

p = Person.objects.create(first_name="Bruce", last_name="Springsteen")

和:

p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)

是等效的.

force_insert参数在其他地方有说明,但它的意思是总是会创建一个新的对象。通常情况下,你不需要担心这个问题。但是,如果你的模型中包含了一个你设置的手动主键值,而且如果这个值已经存在于数据库中,那么对 create() 的调用就会以一个 IntegrityError 失败,因为主键必须是唯一的。如果使用手动主键,要做好处理异常的准备。

3.get_or_create

一个方便的方法,用于查找具有给定 kwargs 的对象(如果你的模型对所有字段都有默认值,则可能为空),必要时创建一个对象

返回 (object, created) 的元组,其中 object检索或创建的对象created 是指定是否创建新对象的布尔值

这是为了防止在并行进行请求时创建重复的对象,并作为样板代码的快捷方式。 例如:

try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
    obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
    obj.save()

在这里,如果是并发请求,可能会多次尝试用相同的参数保存一个 Person。为了避免这种竞争条件,可以使用 get_or_create() 重写上面的例子,比如:

obj, created = Person.objects.get_or_create(
    first_name='John',
    last_name='Lennon',
    defaults={'birthday': date(1940, 10, 9)},
)

任何传递给 get_or_create() 的关键字参数—— 除了 一个叫 defaults 的可选参数——都将在 get() 调用中使用如果找到了一个对象,get_or_create() 返回该对象的元组和 False。 (defaults表示若创建的话,插入吧?)

等等...

常用的数据库操作异常(补充)

www.python.org/dev/peps/pe…

  • DataError:由于处理数据的问题而产生的异常,如除以0,数字值超出范围等。
  • OperationalError:为与数据库操作有关的错误而引发的异常,这些错误不一定在程序员的控制之下,例如,一个意外的断开连接发生,数据源名称没有找到,一个事务不能被处理,一个内存分配错误在处理中发生,等等。
  • IntegrityError完整性错误:当数据库的关系完整性受到影响时引发的异常,例如外键检查失败。
  • InternalError内部错误:当数据库遇到内部错误时产生的异常,例如游标不再有效,事务不同步,等等。
  • ProgrammingError编程错误:当编程错误时产生的异常,例如:没有找到表或已经存在,SQL语句中的语法错误,指定的参数数量错误,等等
  • NotSupportedError不支持的错误:在使用了数据库不支持的方法或数据库API的情况下产生的异常,例如,在不支持事务或关闭事务的连接上请求.rollback()。

image.png