Models介绍

models整体介绍

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

例如:

from django.db import models

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

1)

  • first_name和last_name都是模型的字段
  • 每个字段都是一个类属性
  • 每个类属性都映射一个数据库列。

2)

  • models.CharField()是字段类型
  • 每个字段类型都是models的类实例

3)

  • max_length是字段选项
  • 一些字段选项是通用的,可以用于任何字段类型(见下)

使用模型

如果要将定义的模型迁移到数据库中,需要先将models.py所在的app进行安装:

在settings.py文件中:

INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

然后运行命令:

python manage.py makemigrations
python manage.py migrate

之后上面的Persion模型会创建一个如下的数据库表:

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

表名“myapp_person”是自动生成的,格式为“app名”+“_首字母小写的模型名”。

每个模型生成数据库的时候都会自动添加一个“id”的默认主键字段。

 

常用通用字段选项

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

如果设置为 True ,该字段允许为空。默认为 False

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

  • choices

该参数接收一个可迭代的列表或元组(基本单位为二元组)。如果指定了该参数,在实例化该模型时,该字段只能取选项列表中的值。

一个选项列表:

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

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

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

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'
  • default
该字段的默认值。可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。
  • primary_key

如果设置为 True ,将该字段设置为该模型的主键。

  • unique

   设置字段唯一

  • verbose_name

   设置备注名:

一把Django会自动把首字母转换为大写

first_name = models.CharField("person's first name", max_length=30)
sites = models.ManyToManyField(Site, verbose_name="list of sites")

对于普通字段,第一个位置参数就是备注名,但对于ForeignKey,ManyToManyField和OneToOneField因为第一个字段是关联的模型,所以要使用vsebose_name关键字参数。

 

关联关系

Django定义了三种关联关系:

  • 多对一
  • 多对多
  • 一对一

many to one

 定义一个多对一的关联关系,使用 django.db.models.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, on_delete=models.CASCADE)  #字段名建议设置为需要关联的模型名称
    # ...

many to many

定义一个多对多的关联关系,使用django.db.models.ManyToManyField类。

如,一种Pizza含有多种Topping,而一种Topping也可以存在于多种Pizza中。

from django.db import models

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

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)  #建议多对多字段为一个复数

many to many的模型迁移数据库时会自动的生成第三张表进行关联

扩展many to many关联关系的字段

案例:一个Person模型和一个Group模型,每个Person可以属于多个Group,而每个Group有多个Person,他们之间的关系是多对多的关系。如果在表示他们的关联关系的时候需要加上每个Person加入Group的时间和加入原因,这时就需要扩展关联关系的字段,需要手动自定义第三张中间表:

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)

创建模型实例:

>>> 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>]>

和一般的many-to-many字段不同,这时你不能再使用add(),create(),set()去创建关联关系

>>> # The following statements will not work
>>> beatles.members.add(john)
>>> beatles.members.create(name="George Harrison")
>>> beatles.members.set([john, paul, ringo, george])

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 will not work because it cannot tell which membership to remove
>>> beatles.members.remove(ringo)

不过依旧可以使用clear()方法来移除所有的关联关系:

>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>

定义好关联关系之后就可以进行查询了:

你可以使用关联模型的属性:

# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<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))
<QuerySet [<Person: Ringo Starr]>

直接使用中间表查询信息

>>> 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.'

One-To-One

使用OneToOneField属性定义一对一关系。

可以使用一对一的关系来扩展模型的属性。

案例:有一个Places,Places有地址或邮编属性,在这个地方有一个Restaurant,这个Restaurant也需要地址和邮编属性,这时候就可以借助OneToOne直接扩展Restaurant的属性。

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 "%s the place" % self.name

class Restaurant(models.Model):
    place = models.OneToOneField(
        Place,
        on_delete=models.CASCADE,
        primary_key=True,
    )
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

    def __str__(self):
        return "%s the restaurant" % self.place.name

跨文件关联模型

可以把两个不同的app模型进行关联:

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

Meta选项

from django.db import models

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

元数据不是任何的模型字段

模型方法

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)

常用的两个方法:

__str__():

定义在调用模型的时候自动显示一些字符串;也会自动的显示在admin后台中。

get_absolute_url():

反转URL

模型继承

三种继承方式:

  • 抽象基类
  • Multi-table继承
  • 代理模型

抽象基类

当你有一些通用的信息需要在很多的模型中使用时,可以使用“抽象基类”。“抽象基类”模型不会在数据库迁移的过程中创建数据库;而当它的子类实例化的时候,“抽象基类”定义的通用字段会应用于子模型中。

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模型没有API数据库调用接口,也不会创建数据库。

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'

子类在继承抽象基类的Meta类的时候不会继承abstract=False,即子类不会因为继承了抽象基类而自身也自动变成了抽象基类

小心related_name和related_query_name

 在抽象基类中,如果你在ForeignKey或ManyToMany字段类型中使用了related_name或related_query_name的时候,你需要保证子类的related_name或related_query_name是唯一的。解决办法就是使用'%(app_name)s和‘%(class)s'.

例:

app common/models.py:

from django.db import models

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
app rare/models.py:

from common.models import Base

class ChildB(Base):
    pass

common.ChildA.m2m字段的反向名称是common_childa_related,反向查询名称是common_childas.

common.ChildB.m2m字段的反向名称是common_childb_related,反向查询名称是common_childs.

rare_childB.m2m字段的反向名称为rare_childb_related,反向查询名称是rar_childbs。

默认的related_name为“子类名称”+"_set"。

Multi-table继承

多表继承的情况是子表和父表都有独立的数据库和数据库API接口,它们直接的关系通过自动创建的OneToOneField字段来进行关联。

from django.db import models

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)

Restaurant可以直接引用Place中的所有字段,尽管它们属于不同数据库:

>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")

如果你有一个关联的Place和Restaurant,你可以使用Place对象通过小写的Restaurant引用对应的Restaurant

>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>

在Place和Restaurant之间自动创建的OneToOneField关联自动如下:

place_ptr = models.OneToOneField(
    Place, on_delete=models.CASCADE,
    parent_link=True,
)

你也可以在Restaurant中通过parent_link=True选择自定义OneToOneField字段。

Meta继承

multi-table继承中,子类不会继承父类的Meta属性,但是会继承父类Meta类中的ordering属性和get_latest_by属性,如果不想继承父类的ordering属性,可以通过如下例子关闭:

class ChildModel(ParentModel):
    # ...
    class Meta:
        # Remove parent's ordering effect
        ordering = []
继承和反向关联

因为multi-table继承隐含使用的是OneToOne关联,因此父累也可以变成子累(相当于OneToOne字段类型定义在原先的父类中);但是对于同时存在ForeignKey和ManyToManyField的子类,你必须要指定related_name,不然会和multi-table继承的related_name冲突。

例:

父类仍然使用Place,创建另一个子类

class Supplier(Place):
    customers = models.ManyToManyField(Place)

将会产生错误:

Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.

HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.

代理模型

Proxy models可以让你在不更改源模型的情况下,给源模型添加属性或方法或Meta类属性,也可以通过Proxy model来操作数据库:

from django.db import models

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
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>

 

管理models包

如果models有很多的话,可以使用包进行管理。

方法就是:删除model.py文件----》创建myapp/models/包----》在__init__.py中导入models

例如:

myapp/models/__init__.py¶

from .organic import Person
from .synthetic import Robot

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/kikkiking/p/9956416.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值