Django学习小记[2] —— Model

开始学习django的model了,学习django的目的很简单,就是我想用django搭建一个自己的博客,现在开源的已经有django-zinnia这个博客引擎了,但是想要看懂它,并且修改它,就必须过django这一关。之前对django的了解,仅仅限于用到了什么,就知道什么,缺乏系统的学习,所以要把django的文档都过一遍,做一下简单的笔记。

今天的主题是Model,Model就是MVC中的M,代表的数据对象,反映到数据库中就是数据表,Model中的属性是表的一列,Django对Model进行了封装,对Model提供了丰富的查询接口,反映到数据库中,就是提供了丰富的select查询功能,这就是Django的强大之处。

先来看一个简单的例子,在一个app中的models.py中,定义一个Model:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">from</span> django.db <span class="hljs-keyword" style="color:#f92672;">import</span> models

<span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Person</span><span class="hljs-params" style="">(models.Model)</span>:</span>
    first_name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)
    last_name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)
</code>

所有的Model都继承自django.db.models.Model类,Model类的每一个属性都继承自django.db.models.Field,这个Field有几个作用:

  • 决定该Field在数据库中的类型
  • 决定该Field在前端上如何显示
  • 做简单的验证

Django有很多内置的Field,当然也可以自定义

好,下面我们来重点说一下在Django中如何实现关系型数据库的那三种典型关系:多对一,多对多,一对一

多对一

实现多对一,是使用django.db.models.ForeignKey类,ForeignKey需要一个positional的参数来指定本Model关联的Model,ForeignKey关联的Model是“一”,ForeignKey所在的Model是“多”,比如汽车和制造商的例子,一个汽车只能属于一个制造商,但是一个制造商有多个汽车,这个关系,用Django的Model来表示,就是:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-class" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Manufacturer</span><span class="hljs-params" style="">(models.Model)</span>:</span>
    name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)

<span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Car</span><span class="hljs-params" style="">(models.Model)</span>:</span>
    Manufacturer = models.ForeignKey(Manufacturer)
    name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)
</code>

该关系,用sql语句来表示,就是:

<code class="hljs sql" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-operator" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_manufacturer`</span> (
    <span class="hljs-string" style="color:#e6db74;">`id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> AUTO_INCREMENT <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`name`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>
);</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_car`</span> (
    <span class="hljs-string" style="color:#e6db74;">`id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> AUTO_INCREMENT <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`Manufacturer_id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`name`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>
);</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">ALTER</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_car`</span> <span class="hljs-keyword" style="color:#f92672;">ADD</span> <span class="hljs-keyword" style="color:#f92672;">CONSTRAINT</span> <span class="hljs-string" style="color:#e6db74;">`Manufacturer_id_refs_id_da7168cb`</span> <span class="hljs-keyword" style="color:#f92672;">FOREIGN</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span> (<span class="hljs-string" style="color:#e6db74;">`Manufacturer_id`</span>) <span class="hljs-keyword" style="color:#f92672;">REFERENCES</span> <span class="hljs-string" style="color:#e6db74;">`model_test_manufacturer`</span> (<span class="hljs-string" style="color:#e6db74;">`id`</span>);</span>
</code>

增删操作:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-prompt" style="color:#e6db74;margin-top:0px!important">>>> </span><span class="hljs-keyword" style="color:#f92672;">from</span> model_test.models <span class="hljs-keyword" style="color:#f92672;">import</span> Car
<span class="hljs-prompt" style="color:#e6db74;">>>> </span><span class="hljs-keyword" style="color:#f92672;">from</span> model_test.models <span class="hljs-keyword" style="color:#f92672;">import</span> Manufacturer
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>m = Manufacturer.objects.get(name=<span class="hljs-string" style="color:#e6db74;">"xxx"</span>)
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>m.car_set.all()
[]
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>m.car_set.create(name=<span class="hljs-string" style="color:#e6db74;">"yyy"</span>)
<Car: Car object>
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>c = Car(name=<span class="hljs-string" style="color:#e6db74;">"zzz"</span>)
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>m.car_set.add(c)
</code>

关于多对一更多的内容,参考:Many-to-One

多对多

要实现多对多,就要使用django.db.models.ManyToManyField类,和ForeignKey一样,它也有一个positional的参数,用来指定和它关联的Model。

如果不仅仅需要知道两个Model之间是多对多的关系,还需要知道这个关系的更多信息,比如Person和Group是多对多的关系,除了知道一个Person属于哪个Group之外,如果还想知道这个Person是什么时候加入这个Group的,那么就需要有一个中间表来记录这些信息,那就用到了ManyToManyFiled的一个optional参数: through,如下面的例子:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-class" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Person</span><span class="hljs-params" style="">(models.Model)</span>:</span>                        
    name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)

    <span class="hljs-function" style="color:#f92672;"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">def</span> <span class="hljs-title" style="color:#a6e22e;">__unicode__</span><span class="hljs-params" style="color:#f8f8f2;">(self)</span>:</span>                         
        <span class="hljs-keyword" style="color:#f92672;">return</span> self.name

<span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Group</span><span class="hljs-params" style="">(models.Model)</span>:</span>
    name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)
    members = models.ManyToManyField(Person, through=<span class="hljs-string" style="color:#e6db74;">'Membership'</span>)

    <span class="hljs-function" style="color:#f92672;"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">def</span> <span class="hljs-title" style="color:#a6e22e;">__unicode__</span><span class="hljs-params" style="color:#f8f8f2;">(self)</span>:</span>
        <span class="hljs-keyword" style="color:#f92672;">return</span> self.name

<span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Membership</span><span class="hljs-params" style="">(models.Model)</span>:</span>
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_person = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)
</code>

在中间表中,通过外键关联到Person和Group,其实,就是两个多对一的关系,上面对应的SQL语句为:

<code class="hljs sql" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-operator" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_person`</span> (
    <span class="hljs-string" style="color:#e6db74;">`id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> AUTO_INCREMENT <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`name`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>
)
;</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_group`</span> (
    <span class="hljs-string" style="color:#e6db74;">`id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> AUTO_INCREMENT <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`name`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>
)
;</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_membership`</span> (
    <span class="hljs-string" style="color:#e6db74;">`id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> AUTO_INCREMENT <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`person_id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`group_id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`date_joined`</span> <span class="hljs-keyword" style="color:#f92672;">date</span> <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`invite_person`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>
)
;</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">ALTER</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_membership`</span> <span class="hljs-keyword" style="color:#f92672;">ADD</span> <span class="hljs-keyword" style="color:#f92672;">CONSTRAINT</span> <span class="hljs-string" style="color:#e6db74;">`group_id_refs_id_be33a6a7`</span> <span class="hljs-keyword" style="color:#f92672;">FOREIGN</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span> (<span class="hljs-string" style="color:#e6db74;">`group_id`</span>) <span class="hljs-keyword" style="color:#f92672;">REFERENCES</span> <span class="hljs-string" style="color:#e6db74;">`model_test_group`</span> (<span class="hljs-string" style="color:#e6db74;">`id`</span>);</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">ALTER</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_membership`</span> <span class="hljs-keyword" style="color:#f92672;">ADD</span> <span class="hljs-keyword" style="color:#f92672;">CONSTRAINT</span> <span class="hljs-string" style="color:#e6db74;">`person_id_refs_id_90aaf3d5`</span> <span class="hljs-keyword" style="color:#f92672;">FOREIGN</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span> (<span class="hljs-string" style="color:#e6db74;">`person_id`</span>) <span class="hljs-keyword" style="color:#f92672;">REFERENCES</span> <span class="hljs-string" style="color:#e6db74;">`model_test_person`</span> (<span class="hljs-string" style="color:#e6db74;">`id`</span>);</span>
</code>

对Model进行增加/删除操作:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-prompt" style="color:#e6db74;margin-top:0px!important">>>> </span><span class="hljs-keyword" style="color:#f92672;">import</span> datetime
<span class="hljs-prompt" style="color:#e6db74;">>>> </span><span class="hljs-keyword" style="color:#f92672;">from</span> model_test.models <span class="hljs-keyword" style="color:#f92672;">import</span> Person
<span class="hljs-prompt" style="color:#e6db74;">>>> </span><span class="hljs-keyword" style="color:#f92672;">from</span> model_test.models <span class="hljs-keyword" style="color:#f92672;">import</span> Group
<span class="hljs-prompt" style="color:#e6db74;">>>> </span><span class="hljs-keyword" style="color:#f92672;">from</span> model_test.models <span class="hljs-keyword" style="color:#f92672;">import</span> Membership
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>suo = Person.objects.create(name=<span class="hljs-string" style="color:#e6db74;">"suo"</span>)
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>piao = Person.objects.create(name=<span class="hljs-string" style="color:#e6db74;">"piao"</span>)
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>english = Group.objects.create(name=<span class="hljs-string" style="color:#e6db74;">"English"</span>)
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>m1 = Membership(person=suo, group=english, date_joined=datetime.date(<span class="hljs-number" style="color:#ae81ff;">2014</span>, <span class="hljs-number" style="color:#ae81ff;">9</span>, <span class="hljs-number" style="color:#ae81ff;">9</span>), invite_person=<span class="hljs-string" style="color:#e6db74;">"summer"</span>)           
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>m1.save()
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>m2 = Membership.objects.create(person=piao, group=english, date_joined=datetime.date(<span class="hljs-number" style="color:#ae81ff;">2014</span>, <span class="hljs-number" style="color:#ae81ff;">8</span>, <span class="hljs-number" style="color:#ae81ff;">8</span>), invite_person=<span class="hljs-string" style="color:#e6db74;">"spring"</span>)
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>english.members.all()
[<Person: suo>, <Person: piao>]
</code>

注意,这种形式的多对多,添加两个Model的关系时,不能够通过Model的关联属性直接添加,而应该是创建中间关系的对象,即不能执行这样的操作:

<code style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0px; color:inherit; background-color:transparent; margin-top:0px!important">english.members.add(suo)
</code>

因为添加关系还有其他的属性(date_jointed/invite_person)需要指定,所以不能够直接添加。

一对一

一对一是通过django.db.models.OneToOneField来实现的,被关联的Model会被加上Unique的限制。比如Person和IdCard就是一对一的关系,用Django的Model来表示,就是:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-class" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">IdCard</span><span class="hljs-params" style="">(models.Model)</span>:</span>
    number = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)
    person = models.OneToOneField(Person)

<span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Person</span><span class="hljs-params" style="">(models.Model)</span>:</span>
    name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)

    <span class="hljs-function" style="color:#f92672;"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">def</span> <span class="hljs-title" style="color:#a6e22e;">__unicode__</span><span class="hljs-params" style="color:#f8f8f2;">(self)</span>:</span>
        <span class="hljs-keyword" style="color:#f92672;">return</span> self.name
</code>

对应的SQL语句为:

<code class="hljs sql" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-operator" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_person`</span> (
    <span class="hljs-string" style="color:#e6db74;">`id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> AUTO_INCREMENT <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`name`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>
)
;</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_idcard`</span> (
    <span class="hljs-string" style="color:#e6db74;">`id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> AUTO_INCREMENT <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`number`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`person_id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">UNIQUE</span>
)
;</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">ALTER</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_idcard`</span> <span class="hljs-keyword" style="color:#f92672;">ADD</span> <span class="hljs-keyword" style="color:#f92672;">CONSTRAINT</span> <span class="hljs-string" style="color:#e6db74;">`person_id_refs_id_c2c57084`</span> <span class="hljs-keyword" style="color:#f92672;">FOREIGN</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span> (<span class="hljs-string" style="color:#e6db74;">`person_id`</span>) <span class="hljs-keyword" style="color:#f92672;">REFERENCES</span> <span class="hljs-string" style="color:#e6db74;">`model_test_person`</span> (<span class="hljs-string" style="color:#e6db74;">`id`</span>);</span>
</code>

注意model_test_idcard表的person_id加上了unique限制。

接下来,再来介绍一下Model的继承,有三种:Abstract base classes, Multi-table inheritance, Proxy models

Abstract base classes

Model之间的继承关系其实也就是Python中的继承,子类继承父类中的属性,但是由于每一个Model都对应了数据库中的一个数据表,所以有些地方得需要做一些特殊处理:做为父类的Model要在它的Meta中显示的申明为抽象,否则也会为父类创建数据库表,一般情况下,我们只需要父类做为一个存放公共代码的地方,并不想要它有自己的数据库表。需要注意的是,子类继承父类中的属性,包括Meta中的属性,但是唯独不继承Meta中的abstract属性,如果子类也想要是抽象Model,那么要显示的再次指定该参数。如下面的例子:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-class" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">CommonInfo</span><span class="hljs-params" style="">(models.Model)</span>:</span>
    name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)
    age = models.PositiveIntegerField()

    <span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="color:#a6e22e;font-style:italic">Meta</span>:</span>
        abstract = <span class="hljs-keyword" style="color:#f92672;">True</span>

<span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Student</span><span class="hljs-params" style="">(CommonInfo)</span>:</span>
    score = models.IntegerField()

<span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Teacher</span><span class="hljs-params" style="">(CommonInfo)</span>:</span>
    rating = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)
</code>

对应的SQL语句为:

<code class="hljs sql" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-operator" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_student`</span> (
    <span class="hljs-string" style="color:#e6db74;">`id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> AUTO_INCREMENT <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`name`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`age`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> UNSIGNED <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`score`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>
)
;</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_teacher`</span> (
    <span class="hljs-string" style="color:#e6db74;">`id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> AUTO_INCREMENT <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`name`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`age`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> UNSIGNED <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`rating`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>
)
;</span>
</code>
Multi-table inheritance

这种和上面的区别就是把父类中的Meta的abstract去掉了,也就是说父类也有自己的数据库表,而且这个父类和子类之间是一对一的关系。例子如下:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-class" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Place</span><span class="hljs-params" style="">(models.Model)</span>:</span>
    name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)
    address = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)

    <span class="hljs-function" style="color:#f92672;"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">def</span> <span class="hljs-title" style="color:#a6e22e;">__unicode__</span><span class="hljs-params" style="color:#f8f8f2;">(self)</span>:</span>
        <span class="hljs-keyword" style="color:#f92672;">return</span> self.name

<span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Restaurant</span><span class="hljs-params" style="">(Place)</span>:</span>
    serves_hot_dogs = models.BooleanField()
    serves_pizza = models.BooleanField()
</code>

得到的SQL如下:

<code class="hljs sql" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-operator" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_place`</span> (
    <span class="hljs-string" style="color:#e6db74;">`id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> AUTO_INCREMENT <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`name`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`address`</span> <span class="hljs-keyword" style="color:#f92672;">varchar</span>(<span class="hljs-number" style="color:#ae81ff;">30</span>) <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>
)
;</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">CREATE</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_restaurant`</span> (
    <span class="hljs-string" style="color:#e6db74;">`place_ptr_id`</span> <span class="hljs-keyword" style="color:#f92672;">integer</span> <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span> <span class="hljs-keyword" style="color:#f92672;">PRIMARY</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span>,
    <span class="hljs-string" style="color:#e6db74;">`serves_hot_dogs`</span> bool <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>,
    <span class="hljs-string" style="color:#e6db74;">`serves_pizza`</span> bool <span class="hljs-keyword" style="color:#f92672;">NOT</span> <span class="hljs-keyword" style="color:#f92672;">NULL</span>
)
;</span>
<span class="hljs-operator" style=""><span class="hljs-keyword" style="color:#f92672;margin-top:0px!important">ALTER</span> <span class="hljs-keyword" style="color:#f92672;">TABLE</span> <span class="hljs-string" style="color:#e6db74;">`model_test_restaurant`</span> <span class="hljs-keyword" style="color:#f92672;">ADD</span> <span class="hljs-keyword" style="color:#f92672;">CONSTRAINT</span> <span class="hljs-string" style="color:#e6db74;">`place_ptr_id_refs_id_cc7b5838`</span> <span class="hljs-keyword" style="color:#f92672;">FOREIGN</span> <span class="hljs-keyword" style="color:#f92672;">KEY</span> (<span class="hljs-string" style="color:#e6db74;">`place_ptr_id`</span>) <span class="hljs-keyword" style="color:#f92672;">REFERENCES</span> <span class="hljs-string" style="color:#e6db74;">`model_test_place`</span> (<span class="hljs-string" style="color:#e6db74;">`id`</span>);</span>
</code>

添加一个Place很容易,那么怎么来添加一个Restaurant呢?而且两者是一对一的关系,怎么来添加他们之间的关系呢?记住,Restaurant是Place的子类,继承了Place的属性,所以直接创建Restaurant就可以了:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-prompt" style="color:#e6db74;margin-top:0px!important">>>> </span><span class="hljs-keyword" style="color:#f92672;">from</span> model_test.models <span class="hljs-keyword" style="color:#f92672;">import</span> Restaurant
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>Restaurant.objects.create(name=<span class="hljs-string" style="color:#e6db74;">"r1"</span>, address=<span class="hljs-string" style="color:#e6db74;">"a1"</span>, serves_hot_dogs=<span class="hljs-keyword" style="color:#f92672;">True</span>, serves_pizza=<span class="hljs-keyword" style="color:#f92672;">False</span>)
<Restaurant: r1>
</code>

这样,在数据库中,就会分别向place和restaurant表中各添加一条记录,而且restaurant表中用place id作为主键。

更多关于这个类型的内容见:Multi-table inheritance

Proxy models

这种类型的继承用的比较少,它主要用来在不改变原来Model的行为的情况下,扩展Model的行为,即为原来的Model设置了一个代理,可以重新定义该代理的行为,但是保留原来Model的行为。比如说原来的Model我想让它是一种排序方法,但是我也想让它有另外一种排序方法怎么办?那就为该Model创建一个代理,在这个代理中指定另外一个排序方法,通过这个代理来访问,就可以得到新的排序。

这里有个限制,就是父Model不能是抽象的。举个例子:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-class" style="margin-top:0px!important"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">Person</span><span class="hljs-params" style="">(models.Model)</span>:</span>
    first_name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)
    last_name = models.CharField(max_length=<span class="hljs-number" style="color:#ae81ff;">30</span>)

    <span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="color:#a6e22e;font-style:italic">Meta</span>:</span>
        ordering = [<span class="hljs-string" style="color:#e6db74;">'first_name'</span>]

    <span class="hljs-function" style="color:#f92672;"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">def</span> <span class="hljs-title" style="color:#a6e22e;">__unicode__</span><span class="hljs-params" style="color:#f8f8f2;">(self)</span>:</span>
        <span class="hljs-keyword" style="color:#f92672;">return</span> self.name

<span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="">MyPerson</span><span class="hljs-params" style="">(Person)</span>:</span>
    <span class="hljs-class" style=""><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">class</span> <span class="hljs-title" style="color:#a6e22e;font-style:italic">Meta</span>:</span>
        proxy = <span class="hljs-keyword" style="color:#f92672;">True</span>
        ordering = [<span class="hljs-string" style="color:#e6db74;">'last_name'</span>]

    <span class="hljs-function" style="color:#f92672;"><span class="hljs-keyword" style="color:#66d9ef;margin-top:0px!important">def</span> <span class="hljs-title" style="color:#a6e22e;">do_something</span><span class="hljs-params" style="color:#f8f8f2;">(self)</span>:</span>
        <span class="hljs-keyword" style="color:#f92672;">pass</span>
</code>

在子类中的Meta中设置了proxy=True,就是指定该Model为代理Model,他们两个对应同一个数据库,但是有不同的访问行为。通过MyPerson访问的数据,会按last_name进行排序,而且还可以通过定义新的方法,扩展它的功能。如:

<code class="hljs python" style="font-family:Menlo,Monaco,Consolas,'Courier New',monospace;font-size:undefined; padding:0.5em; color:rgb(248,248,242); background-color:rgb(35,36,31); display:block; margin-top:0px!important"><span class="hljs-prompt" style="color:#e6db74;margin-top:0px!important">>>> </span><span class="hljs-keyword" style="color:#f92672;">from</span> model_test.models <span class="hljs-keyword" style="color:#f92672;">import</span> Person
<span class="hljs-prompt" style="color:#e6db74;">>>> </span><span class="hljs-keyword" style="color:#f92672;">from</span> model_test.models <span class="hljs-keyword" style="color:#f92672;">import</span> MyPerson

<span class="hljs-prompt" style="color:#e6db74;">>>> </span>Person.objects.create(first_name=<span class="hljs-string" style="color:#e6db74;">"suo"</span>, last_name=<span class="hljs-string" style="color:#e6db74;">"guangyu"</span>)
<Person: suo guangyu>
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>Person.objects.create(first_name=<span class="hljs-string" style="color:#e6db74;">"xing"</span>, last_name=<span class="hljs-string" style="color:#e6db74;">"demo"</span>)
<Person: xing demo>

<span class="hljs-prompt" style="color:#e6db74;">>>> </span>Person.objects.all()
[<Person: suo guangyu>, <Person: xing demo>]
<span class="hljs-prompt" style="color:#e6db74;">>>> </span>MyPerson.objects.all()
[<MyPerson: xing demo>, <MyPerson: suo guangyu>]
>>>
</code>

这个类型的继承还是很有用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值