模型语法-Django1.7

(http://django-cn.readthedocs.org/zh_CN/1.7/topics/db/models.html)

模型(Models)

模型是关于你数据的唯一,来源权威的信息。它包含了你保存的基本字段和数据的行为。一般来说,每个模型对应了一个单一的数据表。

基本:

  • 每个模型都是一个django.db.models.Model python类的子类。
  • 每个模型中的属性都代表了一个数据库里的字段。
  • 通过所有模型提供的功能,Django给你一个自动生成的访问数据库的API,详见Making queries.

快速示例(Quick example):

这个示例模型定义了Person,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)

上述代码等于:

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

几点声明:

  • 数据表的名字格式为myapp_person,是自动生成但是可以重写,详见 Table names 。
  • id字段是自动添加的,但是其行为是可以重写的,详见Automatic primary key fields.
  • CREATE TABLE的SQL语言使用了postgreSQL的语法,但是Django会根据你的设置文件settings file对SQL进行定制。

————————

字段选项(Field options)

每个字段拥有一组确定的针对字段的参数(详见 model field reference)。例如, CharField (and its subclasses)需要一个max_length参数,它特别指明在数据库字段中varchar的长度。

null

如果True,Django将会在数据库将空值储存为NULL。默认为False。

blank

如果True,这个字段允许空值,默认为False。注意和null的不同,null是纯粹的与数据库相关,然而blank是关于验证的。如果字段blank=True,表单验证将允许输入空值。

choices

是二元可迭代的,用作字段的选项。如果给出选项,默认表单组建为select box。

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
u'L'
>>> p.get_shirt_size_display()
u'Large'

 

选项字段的第一个元素保存在数据库,第二个元素会显示在默认的表单组件或ModelChoiceField。通过给定的模型对象,可以通过get_FOO_display方法显示选项的值。

default

该字段的默认值. 这里可以是一个具体值或者可以调用的对象。如果是可调用,将会在每次模型对象被创建时调用。

help_text

额外的帮助文本,显示在表单组件旁。即使你不在表单中使用该字段,对文档还是很有用。

primary_key

如果True,这个字段就是该模型的主键。如果你不在任何字段指明primary_key=True,Django会自动添加一个IntergerField作为主键,详见Automatic primary key fields.

unique

如果True,这个字段在整个表中必须是唯一的。

详细字段名(Verbose field names)

 

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

poll = models.ForeignKey(Poll, verbose_name="the related poll")
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(Place, verbose_name="related place")

 

 

 

关系(Relationships)

很明显,关系型数据库的力量来自关联的数据表。Django提供了一个方法,定义三种最常见的数据库关系: many-to-one, many-to-many and one-to-one.

Many-to-one 关系:

定义一个many-to-one关系,使用django.db.models.ForeignKey

ForeignKey 需要一个位置参数: 与该模型联系的模型类名。你还可以创建递归关系 recursive relationships (一个和自己有mang-to-one关系的模型对象) 和 relationships to models not yet defined; 详见 the model field reference

 

from django.db import models

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

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

作为建议,ForeignKey字段的名字最后用模型名字的小写。

Many-to-Many 关系:

为了定义Many-to-Many 关系,使用ManyToManyField。

ManyToManyField需要一个位置参数: 与该模型联系的模型类名。

from django.db import models

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

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

就像 ForeignKey,你也可以创建 recursive relationships (an object with a many-to-many relationship to itself) and relationships to models not yet defined; see the model field reference 。

具体是哪一个模型含有 ManyToManyField没有区别,但你应该只在其中一个模型中写ManyToManyField字段,而不是两个都写。

ManyToManyField 字段还接受一系列额外参数,具体解释详见 the model field reference. 这些可选项定义了这些关系应该如何工作。

many-to-many关系的额外字段

当我们处理简单的many-to-many关系时,标准 ManyToManyField就足够了。然而,有时你需要把两个模型的数据联系起来。例如,思考一下解决音乐人属于什么音乐团体的例子。音乐人和音乐团体之间是many-to-many的关系,然而,还有很多关于成员关系的细节,例如person加入group的日期。

对于这种情况,Django允许你指定那些用于决定manytomany关系的模型。在中间模型设置额外字段。中间模型通过ManyToManyField ,through 参数,和那些将表现的像中间模型的模型联系起来。

 

from django.db import models

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

    def __str__(self):              # __unicode__ on Python 2
        return self.name

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

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Membership(models.Model):    #中间模型
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

 

 一些使用中间模型的限制:...

Django 1.7的更新

Django 1.6或更早的版本,包含多个外键的中间模型和任何模型通过many-to-many关系禁止联系起来。现在,你已经设置了ManyToManyField 使用中间模型,你已经准备好创建一些many-to-many联系了。你可以通过创建中间模型实例做这件事。

 

>>> 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()
[<Person: Ringo Starr>]
>>> ringo.group_set.all()
[<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()
[<Person: Ringo Starr>, <Person: Paul McCartney>]

不像一般的many-to-many字段,你不能使用add,create或赋值 (i.e., beatles.members [...]) 来创建关系:

# THIS WILL NOT WORK
>>> beatles.members.add(john)
# NEITHER WILL THIS
>>> beatles.members.create(name="George Harrison")
# AND NEITHER WILL THIS
>>> beatles.members = [john, paul, ringo, george]

 

why?你不能直接在Person和Group之间建立关系,你需要指明所有Membership模型需要的信息。简单的add,create和赋值调用不能提供额外的信息。所以,仅仅用中间模型无法建立many-to-many关系,唯一的方法是创建中间模型的实例。

remove() 方法也因为同样的原因无法使用。然而,clear()方法可以通过一个实例删除所有的many-to-many关系:

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

一旦你已经通过创建中间模型的实例建立many-to-many关系,你就可以查询Query。就像一般的many-to-many关系一样。

 

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

 

如果你需要获得一些关系信息,你可以直接查询Membership模型。

>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
u'Needed a new drummer.'

 

另一个获得相同信息的方法是通过查询many-to-many reverse relationship 通过一个Person 实例:

>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
u'Needed a new drummer.'

 

One-to-one 关系

为了定义一个one-to-one关系,使用 OneToOneField

例如,如果你构建了一个“place”数据库,你会定义一些标准的字段,比如地址,电话。然后,如果你想在“place”的基础上定义一个“restaurant”的数据库。你可以不需要重复已经在place里定义的字段,而是给Restaurant和Place建立一个one-to-one关系(因为一个Restaurant “is a”Place;你还可以使用模型继承inheritance)。

OneToOneField 字段还接受一个特殊,可选的, parent_link 参数,详见 model field reference.

OneToOneField 类过去自动生成为模型主键。现在不是这样的。现在可以在一个模型拥有多个OneToOneField字段。

跨文件使用模型

完全可以和另一个app里的模型建立联系。你需要首先import该文件。

 

from django.db import models
from geography.models import ZipCode

class Restaurant(models.Model):
    # ...
    zip_code = models.ForeignKey(ZipCode)

 

 

 

字段命名限制

Django有两个关于字段命名的限制:

1、不可以时python的保留关键词。

2、字段名里不可以有连续两个下划线。

自定义字段类型

如果一个已有的模型不能满足你的需求,或者你希望使用一些不太常见的数据库列类型,你可以创建自己的字段类型。详见Writing custom model fields.

Meta 选项

可以通过class Meta 创建你的模型元数据。

from django.db import models

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

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

模型元数据是“anything that's not a field”,比如排序选项(ordering),数据表命名(db_table), 或者可读性强的名字 (verbose_name and verbose_name_plural)。这些选项都不是必须的,包括添加class Meta。一个完整的Meta选项列表见,model option reference.

模型属性

objects:最常用的模型属性是 Manager这是通过数据库查询操作提供给Django的模型接口,并可以从数据库中查找实例( retrieve the instances).如果没有自定义Manager,默认名字是objects。Manager只能通过模型查询,不能通过模型实例查寻。

这是一个把有利于把业务逻辑集中在模型中的很有价值的技术。

 

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"

    def _get_full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)
    full_name = property(_get_full_name)

 

 

 最后一个方法是 property.

model instance reference 拥有一个完整列表 methods automatically given to each model. 你可以重写其中的大多部分 – see overriding predefined model methods, below – 但是下列方法总是有用的:

__str__() (Python 3)Python 3 equivalent of __unicode__().

__unicode__() (Python 2)

一个Python的“魔术方法”,返回一个unicode字符串代表对象。Python和Django当一个模型实例需要被强制显示为字符串都会使用。它经常会使用在交互控制台或者admin中。

get_absolute_url()

这个方法告知Django如何为一个对象计算URL。Django会在admin的交互环境中使用它,或者所有需要获得一个对象的URL时。所有有唯一标识URL的对象都应该定义这个方法。

重写预定义的模型方法

有一系列model methods,它们包含了很多你会希望定制的数据库动作。特别的,你总会希望改变save() and delete()你可以任意重写这些方法。

一个经典的重写内置方法的应用是你希望当你保存一个对象发生一些操作。

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
        do_something_else()、
#You can also prevent saving:
from django.db import models class Blog(models.Model): name = models.CharField(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(Blog, self).save(*args, **kwargs) # Call the "real" save() method.

 

super(Blog, self).save(*args, **kwargs)可以保证对象却是保存到数据库里。如果你忘记调用父类的save()方法,将无法保存到数据库。另外,传递给父类那些传给模型方法的参数也是很重要的,也就是指*args, **kwargs。Django偶尔会拓展新功能给内置模型方法,添加新的参数,如果你在你的方法定义中使用*args, **kwargs,你就可以确保你的代码将自动支持新添加的参数。

执行自定义SQL

另外的一般模式是在模型方法和模块方法中写自定义SQL语句。对于使用原生SQL语句,详见 using raw SQL

模型继承

Django中的模型继承和Python中的类继承十分类似。但是基类应该是django.db.models.Model的子类

重要的是,你需要决定父模型是作为拥有自己数据表的模型或者仅仅是为了保存一般性信息,仅仅通过子模型可见。下列是三条在Django中的继承风格。。。。

抽象基类

抽象基类在当你想在很多模型中保存相同的一般性信息是很有用。你写自己的基类并在Meta中添加abstract=True。这个模型就不会生成自己的数据表。当子类和父类有同名的字段时会产生一个error。

 

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)

 

 

 

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'

 

 

 

Django会对抽象基类的Meta做一个调整:安装Meta属性之前,abstract=False。这意味着抽象基类的子类不会自动变成抽象类。当然,你可以通过从另一个抽象基类继承创建一个抽象基类。你只是需要明确定义abstract=True。注意改变继承子类的类似db_table之类的属性。

注意related_name

 如果你在ForeignKey或 ManyToManyField使用related_name 属性,你总是为字段定义一个唯一的反向名字。只要这个类的字段用在每一个子类里,并且每个属性的值都相同,这一般会在抽象基类里引起一个问题。为了解决这个问题,当你在抽象基类中使用related_name,名字的一部分应该包含 '%(app_label)s' and '%(class)s'.

  • '%(class)s'会被应用这个字段的子类的小写名称代替。
  • '%(app_label)s'会被子类所在的app的小写名称代替。每个app的名字必须是唯一的,并且每个app里的模型类的名称必须唯一。

For example, given an 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")

    class Meta:
        abstract = True

class ChildA(Base):
    pass  #reverse name = common_childa_related

class ChildB(Base):
    pass  #reverse name = common_childb_related

 

 

 

 Along with another app rare/models.py:

from common.models import Base

class ChildB(Base):
    pass  #reverse name = rare_childb_related

 

多表继承

Meta与多表继承

继承与反向关系

指定父链接字段

代理模型

查询集仍然返回请求的模型

基类的限制

代理模型管理器

代理继承和非托管模型之间的差异

多重继承

字段名“hiding”是不允许的

 

posted @ 2014-12-05 14:11  落叶落叶  阅读(241)  评论(0)    收藏  举报