models数据模型学习笔记

        每一个应用通常都会定义一个或多个models,这个数据models其实是与数据库相关的,models中的每一个属性都是数据库当中的一个字段,每一个字段是数据库中的一个列。在models中定义的每一个类至关于数据库当中的table.如html

 

 

[python] view plain copypython

  1. class  Musician(models.Model):  
  2.   First_name = models.CharField(max_length = 50 )  
  3.   Last_name = models.CharField(max_length = 50 )  
  4.   Instrument = models.CharField(max_length = 100 )  
  5.     
  6. class Album(models.Model):  
  7.   Artist = models.ForeignKey(Musician)  
  8.   Name = models.CharField(max_length = 100)  
  9.   Release_date = models.DateField()  
  10.   Num_starts = models.IntegerField()  

 

 

        如上所示,每一个class的属性都是models中的某些字段类的实例,如Name是models.CharField的实例。Models中有许多的字段属性类。而且这些字段属性类均有一些参数,用于修饰这些class内的属性,如Name是CharField的实例,限定其最大字符为100字节.那么,这些字段属性类都有哪些参数呢?下面列出一些经常使用的参数:数据库

null:若为true,dnango将在数据库相应的字段中容许存储null值,不然设为flasedjango

blank: 与null有点相似,可是不一样,它主要用于确认一些如表单中是否带有一些内容数据,若是blank设为true,则容许表单内容为空,不然设为flase.app

choices:带此参数的字段通常为2维的列表或元组,类中的属性在定义时若含此参数,在页面上将显示一个select box框,其选项也被限定于choices给定的具体值,以下:ide

[python] view plain copy函数

  1. YEAR__IN_SCHOOL_CHOICES = (  
  2.  (‘FR’, ‘Freshman’),  
  3.  (‘SO’, ‘Sophomore’),  
  4.  (‘JR’, ‘Junior’),  
  5.  (‘SR’, ‘Senior’),  
  6.  (‘GR’, ‘Graduate’),  
  7.  )  

        注:每一个元组中的第一个参数如’FR’等将被存于数据库中,第二个参数将显示在select box中。对于一个给定的model对象实例,choices字段显示的值能够经过get_YOURFIELD_display方法来访问,以下:url

[python] view plain copyspa

  1.   From django.db import models  
  2.   Class Person(models.Model):  
  3.       SHIRT_SIZES = (  
  4.   (‘S’, ‘Small’),  
  5.   (‘M’, ‘Medium’),  
  6.   (‘L’, ‘Large’),  
  7.       )  
  8.       name = models.CharField(max_length = 60 )  
  9.   shirt_size = models.CharField(max_length = 1, choices = SHIRT_SIZES)  
  10.     
  11. >>>p = Person(name =”zhm”, shirt_size = ‘L’)  
  12. >>>p.save()  
  13. >>>p.shirt_size  
  14. u’L’  
  15. >>>p.get_shirt_size_display()  
  16. u’Large’   

default: 字段的默认值,能够是个值或者是个callable的对象。若是是callable的,在每次新对象被建立时,它将被调用。.net

 

help_text: 

primary_key: 设为true时为model的primary_key.若不设置,则系统会自动添加一个IntegerField字段为primary_key,每一个model必需要有一个字段被设置成primary_key = true.

 

自动的主键字段:默认时,django会给每一个model下面一个字段:

Id = models.AutoField(primary_key = true).这是一个自增的主键,若是要覆盖系统的主键字段,能够在model中的任何字段中设置primary_key = true便可。

 

Verbose field names(冗长的字段名称)

        除了ForeignKey, ManyToManyField, OneToOneField外,其它每种字段类型如CharField均有一个可选的第一个固定参数:Verbose Name.若是没有给出这个参数值,django会自动的建立它,而自动建立的verbose name=字段实例名.以下:

 

[python] view plain copy

  1. first_name = models.CharField(“person’s first name”, max_length = 30)  

 

此时verbose name = “person’s first name”

而当

 

[python] view plain copy

  1. first_name = models.CharField(max_length = 30)  

 

它的verbose name = “first name”

另外,因为ForeignKey, ManyToManyField及OneToOneField其第一个参数应为一个model class,因此它的verbose name应该是显示给出:

 

[python] view plain copy

  1. Poll = models.ForeignKey(Poll, verbose_name = “the related poll”)  
  2. Sites = models.ManyToManyField(Site, verbose_name = “list of sites”)  
  3. Place = models.OneToOneField(Place, verbose_name = “related place “)  

 

 

Relationships

        主要有三类:many-to-one, many-to-many, one-to-one

Many-to-one 关系

        要定义一个多对一的关系,使用django.db.models.ForeignKey.其使用方法与其它字段类型同样,如CharField,没有任何区别,ForeignKey须要一个固定的参数,基于某个关系模型的类,举个例子,好比一辆汽车与工厂的关系,工厂能够建立许多的汽车,可是一辆汽车只能属于某一个工厂。因此能够用如下方式来建立:

[python] view plain copy

  1. class Manufacturer(models.Model):  
  2.   #...  
  3. class Car(models.Model):  
  4.   manufacturer = models.ForeignKey(Manufacturer)  
  5.   #...  

        汽车是因为某个工厂建立的,因此Car class中须要增长工厂的相关信息,这种关系表示为ForeignKey. 而它的第一个参数为Manufacturer,是一个model的名称,这个ForeignKey也能够接收其它参数,用于定义这些关系如何发生做用,不过这些参数都是可选的,之后本身能够查看相关资料。

 

Many-to-many关系

        定义多对多关系可使用ManyToMany字段类型。其使用方法与其它字段类型一致。与ForeignKey同样,它也要接受一个固定的参数,一个与之发生关系的model class的名称。能够举这么个例子来描述多对多的关系,好比说一个月饼上有许多的芝麻,而芝麻也能够存在于多个月饼之上,因此它们的关系就能够按如下方式来表示:

[python] view plain copy

  1. class MoonCake(models.Model):  
  2.   #...  
  3. class Sesame(models.Model):  
  4.   mooncakes = models.ManyToMany(MoonCake)  
  5.   #...  

        建议ManyToMany对象以复数形式来表达一组相关的多个model对象,如上的mooncakes.此外,ManyToMany能够在任意两个model中,这个没有任何关系,可是不能在两个模型内均存在,即上面的ManyToMany也能够定义在MoonCake内:sesames = models.ManyToMany(Sesame)

        通常来讲,ManyToMany实例应该定义在那些将要在表单(form)中被编辑的对象内。如上面的月饼与芝麻的关系中,咱们更能想到的是,月饼拥有不少芝麻,而更少想到芝麻在多个月饼上,因此上面的关系是经过这种想法定义的,故在芝麻中定义ManyToMany.这样MoonCake表单可让用户来选择那些芝麻。

 

更复杂的Many-to-many关系:

        简单的表述就是一我的,它能够参加各类兴趣小组,即每种兴趣小组有许多的人员,这显然是多对多的关系,可是除此以外,还须要其它的额外的信息,好比关于人员加入小组的时期,加入的缘由等,因此须要一个中间耦合的modle来进行链接。具体代码能够表述以下:

[python] view plain copy

  1. class Person(models.Model):  
  2.     name = models.CharField(max_length = 128)  
  3.     def __unicode__(self):  
  4.         return self.name  
  5.   
  6. class Group(models.Model):  
  7.     name = models.CharField(max_length=128)  
  8.     members = models.ManyToMany(Person, through = ‘Membership’)  
  9.     def __unicode__(self):  
  10.         return self.name  
  11.      
  12. class Membership(models.Model):  
  13.     person = models.ForeignKey(Person)  
  14.     group = models.ForeignKey(Group)  
  15.     date_joined = models.DateField()  
  16.     invite_reason = models.CharField(max_length = 64)  

 

        请注意Group当中的”through”参数。当创建了一个中间model时,如上面的Membership,将显式的用外键foreignkey指定涉及到多对多关系的那些models.这些显式的声明定义了这两个models的关系是怎样的。下面来实际使用上面定义的模型:

[python] view plain copy

  1. >>> ringo = Person.objects.create(name = ‘Ringo Starr”)  
  2. >>> paul = Person.objects.create(name = “Paul McCartney”)  
  3. >>> beatles = Group.objects.create(name = “The Beetles”)  
  4. >>> m1 = Membership( person = ringo, group = beatles,  
  5. ...       date_joined = date(1962, 8, 16)  
  6. ...       invite_reason = “Needed a new drummer”)  
  7. >>> m1.save()  
  8. >>> beatles.members.all()  
  9. [<Person: Ringo Starr>]  
  10. >>> ringo.group_set.all()  
  11. [<Group: The Beatles>]  
  12. >>> m2 = Membership.objects.create(person = paul, group = beatles,  
  13. ...      date_joined = date(1960,8,1)  
  14. ...      invite_reason = “Wanted to form a band.”)  
  15. >>> beatles.member.all()  
  16. [<Person: Ringo Starr>],[<Person: Paul McCartney>]  

 

        与简单正常的多对多关系不同,不能使用add, create或者赋值来建立这种关系对象,如:

 

[html] view plain copy

  1. beatles.members.add(john)  
  2. atles.members.create(name=”George Harrison”)  
  3. Beatles.members = [join,paul,ringo, george]  

 

        以上这种方式对复杂的多对多关系是不可行的。这是由于直接建立建立Person与Group之间的对象关系没法表达其他额外的信息,这些信息须要由Membership模型来提供。因此建立这种多对多关系对象的惟一方式就是建立中间件model对象的实例。因此remove()方法也不适合于复杂的多对多对象,相应有一个替代的函数clear(),

[python] view plain copy

  1. >>>beatles.members.clear()  

这是可行的。

 

        建立复杂多对多模型后的对象查询方式以下:

[python] view plain copy

  1. >>> Group.objects.filter(members__name__startswith=’Paul’)  
  2. [<Group: The Beatles>]  
  3.   
  4. >>> Person.objects.filter(group__name=’The Beatles’, membership__date_joined__gt = date(1961,1,1))  
  5. [<Person: Ringo Starr>]  
  6.   
  7. >>> ringos_membership = Membership.objects.get(group = beatles, person=ringo)  
  8. >>> ringos_membership.date_joined  
  9. datetime.date(1962,8,16)  
  10. >>> ringos_membership.invite_reason  
  11. u’Needed a new drummer  
  12.   
  13. >>> ringos_membership = ringo.membership_set.get(group = beatles)  
  14. >>> ringos_membership.date_joined  
  15. datetime.date(1962,8,16)  
  16. >>> ringos_membership.invite_reason  
  17. u’Needed a new drummer  

One-to-one关系

        定义这种关系使用OneToOneField.须要一个固定参数:某个关系model的class,这种关系能够理解成继承的关系,好比建立了一个’place’的数据库,里面含有address, phone等标准的信息,若是你再建立一个‘餐馆‘数据库,它是创建在’place’数据库之上的,由于‘餐馆’也须要一些地址信息,因此将‘餐馆’用OneToOneField定义到’place’之上。

 

跨文件的models

        能够引用来自另外一个应用的model,只须要将另外一个应用的model import进来,而后就可使用了。见以下例子:

[python] view plain copy

  1. from geography.models import ZipCode  
  2. class Restaurant(models.Model):  
  3.   #...  
  4.     zip_code = models.ForeignKey(ZipCode)  

 

字段名称的限制

1 不能是Python保留的关键字

2 不能含有2个‘_’,如:

 

[python] view plain copy

  1. Class Example(models.Model):  
  2. foo__bar = models.IntegerField() #error  

 

固然,django也是容许咱们自定义字段类型,能够查阅相关文档。

 

Meta Options

经过使用内部嵌套的class Meta能够给定model的metadata,以下:

 

[python] view plain copy

  1. Class ox(models.Model):  
  2.   Horn_length = models.IntegerField()  
  3.       
  4.   Class Meta:  
  5.       ordering = [‘horn_length’]  
  6.       verbose_name_plural = “oxen”  

 

此Meta的各类options有不少,能够查看相关资料。

 

Model methods

须要知道的常常会用到的一系列函数方法:

__unicode__()

返回unicode表示的对象,这是python与django使用的,当model实例须要被强迫或显示成文本字符时。

 

get_absolute_url():告诉django怎样计算对象的url,django通常在它的admin接口上使用,不管什么时候admin都须要知道一个对象的url.

 

覆盖预约义的model 方法

便可以定制一些model的方法,常常要改变的是save()和delete()工做。

当在保存一个对象时你但愿要进行一些其它额外的操做时,能够改写成以下方式:

[python] view plain copy

  1. Class Blog(models.Model):  
  2.   #....  
  3.   def save(self, *args, **kwargs):  
  4.       do_something()  
  5.       super(Blog, self).save(*args, **kwargs) # call the real save()method  
  6.       do_something_else()  

 

Model继承

    与python中正常的类继承差很少,但要分清的是你是否要让父类的model成为它各自独立的model(拥有各自独立的数据库表),仍是要让父类保存通讯的数据信息,这样就能够在子model中可视了。

    在django中一共有三种方式的model继承:

    1 父model用来保存信息,这样就没必要要输入到子model中,因此这时父类不会以独立的方式使用,这种方式为抽象基类abstract base classes方式

    2 若是你是现有model的子类(有可能来自另外一个应用),须要让每一个model都拥有它自己的数据库表,mutil-table多表继承采用的是这种方式

    3 最后,若是你要修改python级的model, 而不改变model的各字段,你可使用Proxy models方式。

Abstract base classes

    当你须要把一些共享的信息放入某个model中时,使用抽象基类的继承方式是很是有用的。按正常的方式先写好model,而后增长clss Meta,将属性abstract设为true便可。而后对于其它的model,要继承于抽象基类,这样抽像model的字段会被添加到子类中。以下:

[python] view plain copy

  1. class CommonInfo(models.Model):  
  2.   name = models.CharField(max_length = 100)  
  3.   age = models.PositiveIntegerField()  
  4.     
  5.   class Meta:  
  6.       Abstract = True  
  7.     
  8. class Student(CommonInfo):  
  9.   home_group = models.CharField(max_length = 5)  

 

    这样Student就有三个字段:name, age, home_group,因此CommonInfo不能够用做正常的Django model,由于它只是一个抽象基类,它并不生成一个数据库,不能直接实例化或直接保存数据。因此这种抽象基类是很是有用的,它以python的方式提供了一种方法来提取出公共信息,同时仅当子model在建立数据库时才会建立相应的数据。

 

Meta的继承

    当抽象基类被建立,django的作法是让基类内部的Meta子类做为一个可用的属性。若是子类没有声明它本身的Meta类型,这将从父类的Meta中继承过来,若是子类要扩展父类的Meta,能够再进行子类化,以下例子

[python] view plain copy

  1. class CommonInfo(models.Model):  
  2.   name = models.CharField(max_length = 100)  
  3.   age = models.PositiveIntegerField()  
  4.     
  5.   class Meta:  
  6.       abstract = True  
  7.   Ordering = [‘name’]  
  8.     
  9. class Student(CommonInfo):  
  10.   home_group = models.CharField(max_length = 5)  
  11.     
  12.   class Meta(CommonInfo.Meta):  
  13.       db_table = ‘student_info’  

    Django的确对抽像基类中的Meta进行过一次修改,在子类进行安装Meta属性以前,它对abstract设置为false,这意味着子类不会自动的成为抽像基类,固然,若是咱们本身想让子类成为抽像基类的话,能够显式地设置abstruct为true便可。

    注:有时有些属性在将抽像类中的Meta类包含进来时会发生一些没法很差理解的行为。如包含db_table意味着全部的子类(没必要描述他们本身的Meta)将使用基类全部的相同数据库表,这让子类也成为abstract了,这就不是咱们想要的了。

 

当心related_name属性参数

    若在ForeignKey或ManyToManyField中使用related_name属性,必须让字段名称具备惟一性。不然将会引发一些问题,由于这个字段将会被包含到各个子类中,而每次此属性字段都具备相同的值。

    要解决这个问题,在使用related_name在抽像类中时,注意,仅仅针对抽像类,名称的部分要包含’%(app_label)s’和’%(class)s’。

    1 ‘%(class)s’ 将会被使用这个字段的底层类的名称所代替。

    2 ‘%(app_label)s’将会被子类所在的底层应用名称所代替。每一个安装的应用都是惟一的,应用内的每一个model名称也是惟一的。

    如如下例子,给定一个应用app common/models.py:

[python] view plain copy

  1. class Base(models.Model):  
  2.   m2m=models.ManyToManyField  
  3.           (OtherModel, related_name = “%(app_label)s_%(class)s_related”)  
  4.       
  5.   class Meta:  
  6.       Abstract = True  
  7.   
  8. class ChildA(Base):  
  9.   Pass  
  10. class ChildB(Base):  
  11.   Pass  

    接着还有另外一个应用rare/models.py;

[python] view plain copy

  1. from common.models import Base  
  2. class ChildB(Base):  
  3.     pass  

 

    这样,ChindA当中的m2m名称将是common_childa_related, 同时ChindB中的m2m名称为common_childb_related,而另外一应用rare中的childB中的m2m为rare_childb_related.这样能够保证惟一性。

    注意:若是不指定related_name属性,系统会有默认的名称,能够查看相关资料。

 

 

Multi-table inheritance

Django支持的第二种model继承方式是继承父类的子model是各自独立的,每种模型与其数据库表相关联,可进行分别独立地建立与查询操做。这种继承关系将子model与它的每一个父model进行了链接(django自动采用OneToOneField的方式进行链接),以下:

[python] view plain copy

  1. class Place(models.Model):  
  2.   name = models.CharField(max_length=50)  
  3.   address = models.CharField(max_length=80)  
  4.     
  5. class Restaurant(Place):  
  6.   serves_hot_dogs = models.BooleanField()  
  7.   serves_pizza = models.BooleanField()  

 

虽然数据保存在这两个不一样的数据库表中,但Place中的全部字段在Restaurant中都可被访问,(也能够这么理解:Place主要存储全部相似于Restaurant各类商店的地址,Restaurant对象只能访问其自己在Place中存放的地址,而Place则能够访问全部商店的地址和名称),见以下例子:

[python] view plain copy

  1. >>> Place.objects.filter(name=”Bob’s Cafe”)  
  2. >>> Restaurant.objects.filter(name=”Bob’s Cafe”)  

 

Place包含着一个Restaurant的地址,既然知道了Restaurant的地址,因此咱们能够从Place对象访问到Restaurant对象,访问的方式是使用小写的Restaurant model名称以下:

[python] view plain copy

  1. >>> p = Place.objects.get(id = 12) #先获取Restaurant对象的地址  

 

[python] view plain copy

  1. #若是p是一个Restaurant对象,即p是一个子类  
  2. >>>.p.restaurant #请注意,是小写,这样能够直接访问Restaurant对象  
  3. <Restaurant:....>  

 

       因此咱们能够得出结论,有两种状况,

       1若是p仅仅是Place的一条记录,且这条记录并非Restaurant关联的,即这种记录是Place独有的。

       2 若p这条记录不是Restaurant的地址,而是其它商店如Apple店的地址

在上述两种状况下经过p.restaurant访问则会出现问题了。

 

 

关于Mutil-table inheritance中的Meta

在这种多表继承中的状况下,若是子类从父类中继承它的Meta class,这种状况是不合理的。全部的Meta Option都是应用于它的父类,若是继承到子类中会产生一些冲突(这种状况与抽象基类相反,抽象基类并不存在这种方式)

所以子model并不访问它的父类的Meta class, 可是也有一些少量状况能够从父类继承,好比,若是子类并无给出ordering属性或者是get_latest_by属性,那么这些属性将从他们的父类Meta class中继承。

可是若是父类具备ording且你不想从父类中继承它的ording,能够显式的将其设置为disable,以下:

 

[python] view plain copy

  1. class ChildModel( ParentModel):  
  2.     ...  
  3.     class Meta:  
  4.         #remove parent’s ording effect  
  5.         Ordering = []  

 

Inheritance and reverse relations

因为多表继承使用隐式的OneToOneField来链接父子model,如上例所示这可让父model访问至子model,可是,若是对于使用了一些如ForeignKey和ManyToManyField关系的model而言,这些关键字(ForeignKey和ManyToManyField)所带的参数related_name的值是默认的,若是你想把这些关系继承至其它model的子类时,必须在这些字段中显示的提供related_name属性,若是咱们不这么操做,django在校验validata或者syncdb操做时会引起一个error,如:

[python] view plain copy

  1. Class Supplier(Place):  
  2.   #必须在全部的关系中说明related_name  
  3.   Customers = models.ManyToManyField(Restaurant, related_name = “provider”)  

 

父model的链接字段说明

        如上提到,若是在基于非抽象型数据关系model时,django将在父子model中自动的建立OneToOneField以链接二者,这样子model能够访问父model相应的字段,从父model中的对象也能够间接的访问子model的对象,可是若是在子model中想要更改父model中所提供的字段名称时,能够本身显式的用OneToOneField(在参数中添加设置parent_link = True)来建立这种关系,以链接至父model.

 

Proxy models

        代理模型。代理model继承的目的在于为原始的model建立一个代理,咱们能够进行建立,删除,更新代理模型的实例,而且操做的结果将会保存在原始模型中,即,经过代理摸型间接的操做了原始模型。区别在于经过代理模型,不须要修改原始模型就能够直接改变原始模型的一些东西,好比改变默认的model ordering或者默认的manager等。

代理model的声明与正常的model没什么区别,只是要告诉django它是一个proxy的,方式为在Meta中添加proxy属性为True.

例如,例设咱们如今要对前面提到的Person model添加一个方法,能够以下操做:

[python] view plain copy

  1. class MyPerson(Person):  
  2.     class Meta:  
  3.         Proxy = True  
  4. def do_something(self):  
  5.     ...  

        上面定义的MyPerson model,对它实例的操做结果能够发生在它的父类Person身上,特别的,Person的新对象也能够经过MyPerson来访问,反之亦然:

[python] view plain copy

  1. >>> p = Person.objects.create(first_name=”foobar”)  
  2. >>> MyPerson.objects.get(first_name=”foobar”)  
  3. <MyPerson: foobar>  

 

咱们能够经过代理模型对原模型进行不一样方式的排序,以下,能够增长last_name属性进行排序:

[python] view plain copy

  1. class OrderedPerson(Person):  
  2.   class Meta:  
  3.       ordering = [“last_name”]  
  4.       proxy = True  

 

        正常的原始Peson model的查询是无序的,但OrderedPerson查询后的排序是基于last_name来进行。咱们不可能经过Person来查询并让django返回MyPerson对象。另外,用代理并非说是以一种本身建立的模型来取代Person model的方式。

父类(基类的)一些约束

        Proxy model必须从一个(注意:是一个)非抽象的model类中继承,不能从多个非抽象model中继承,由于proxy model并无在不一样的数据库表的多行之间提供一些链接,proxy model也能够从一些抽象基类中继承,前提条件是他们不能定义model字段。

Proxy model会从非抽象父类model中继承一些他们没有定义的Meta选项.

  

Proxy model managers

        若是没有详细的给出proxy model的模型管理器(manager),那它将从父model中继承,若是在proxy model中定义了管理器,那么它将取代父model的管理器,成为默认的管理器,可是在父类中定义的这些管理器也仍是能够访问的。

如下是上述的例子,当你查询Person model时你能够改变它的默认管理器:

 

[python] view plain copy

  1. class NewManager(models.Manager):  
  2.     ...  
  3. class MyPerson(Peson):  
  4.     objects = NewManager()  
  5.     class Meta:  
  6.       Proxy = True   

 

    若是咱们想增长新的管理器至Proxy,而不会取代现有的默认管理器,可使用相关文档描述的技术细节,它的主要方法是,建立一个包含新的管理器的基类,而后继承它,紧接在primary基类以后,以下:

 

  1. #为新管理器建立一个抽象类  
  2. class ExtraManagers(models.Model):  
  3.     secondary = NewManager()  
  4.     class Meta:  
  5.         Abstract = True  
  6. class MyPerson(Person, ExtraManagers): #紧接在Person以后  
  7.     class Meta:  
  8.          proxy = True;  

    咱们可能不会常常这样作,可是有时也有须要的时候,因此这里先了解一下。

 

关于Multiple inheritance

    与python的子类同样,Django的model能够从多个父model中继承,正常的python命名规则也一样适用,继承的第一个基类的Meta将被使用,其他基类的Meta将会被忽略。

通常来讲,不多会从多个父model中继承,主要的使用原则就是越简单越好,这样能够避免老是去寻找一些特殊的来自其它类中的信息。

 

Models的字段名是不能被隐匿的

    在正常的python继承状况下,它容许子类覆盖父类的一些属性,而在Django,这是不容许的,由于若是一个基类model有一个字段称为author,那么在你建立的另一个model中,若是它继承了前面这个基类,则它是不能再建立一个名为author的字段。若是你覆盖一个父类当中的字段,django会产生一个FieldError错误。

相关文章
相关标签/搜索