models数据模型学习笔记

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

class  Musician(models.Model):
  First_name = models.CharField(max_length = 50 )
  Last_name = models.CharField(max_length = 50 )
  Instrument = models.CharField(max_length = 100 )
  
class Album(models.Model):
  Artist = models.ForeignKey(Musician)
  Name = models.CharField(max_length = 100)
  Release_date = models.DateField()
  Num_starts = models.IntegerField()

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

null:若为truednango将在数据库相应的字段中允许存储null值,否则设为flase

blank: 与null有点类似,但是不同,它主要用于确认一些如表单中是否带有一些内容数据,如果blank设为true,则允许表单内容为空,否则设为flase.

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

 YEAR__IN_SCHOOL_CHOICES = (
  (‘FR’, ‘Freshman’),
  (‘SO’, ‘Sophomore’),
  (‘JR’, ‘Junior’),
  (‘SR’, ‘Senior’),
  (‘GR’, ‘Graduate’),
  )

        注:每个元组中的第一个参数如’FR’等将被存于数据库中,第二个参数将显示在select box中。对于一个给定的model对象实例,choices字段显示的值可以通过get_YOURFIELD_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 =”zhm”, shirt_size = ‘L’)
>>>p.save()
>>>p.shirt_size
u’L’
>>>p.get_shirt_size_display()
u’Large’ 

default: 字段的默认值,可以是个值或者是个callable的对象。如果是callable的,在每次新对象被创建时,它将被调用。

 

help_text: 

primary_key: 设为true时为modelprimary_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=字段实例名.如下:

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

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

而当

first_name = models.CharField(max_length = 30)

它的verbose name = “first name”

另外,由于ForeignKey, ManyToManyFieldOneToOneField其第一个参数应为一个model class,所以它的verbose name应该是显示给出:

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

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

Many-to-one 关系

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

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

        汽车是由于某个工厂创建的,所以Car class中需要增加工厂的相关信息,这种关系表示为ForeignKey. 而它的第一个参数为Manufacturer,是一个model的名称,这个ForeignKey也可以接收其它参数,用于定义这些关系如何发生作用,不过这些参数都是可选的,以后自己可以查看相关资料。


Many-to-many关系

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

class MoonCake(models.Model):
  #...
class Sesame(models.Model):
  mooncakes = models.ManyToMany(MoonCake)
  #...

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

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


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

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

  class Person(models.Model):
      name = models.CharField(max_length = 128)
      def __unicode__(self):
          return self.name
  
  class Group(models.Model):
      name = models.CharField(max_length=128)
      members = models.ManyToMany(Person, through = ‘Membership’)
      def __unicode__(self):
          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)

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

  >>> ringo = Person.objects.create(name = ‘Ringo Starr”)
  >>> paul = Person.objects.create(name = “Paul McCartney”)
  >>> beatles = Group.objects.create(name = “The Beetles”)
  >>> 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.member.all()
  [<Person: Ringo Starr>],[<Person: Paul McCartney>]

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

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

        以上这种方式对复杂的多对多关系是不可行的。这是因为直接创建创建PersonGroup之间的对象关系无法表达其余额外的信息,这些信息需要由Membership模型来提供。所以创建这种多对多关系对象的唯一方式就是创建中间件model对象的实例。所以remove()方法也不适合于复杂的多对多对象,相应有一个替代的函数clear()

  >>>beatles.members.clear()

这是可行的。


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

  >>> Group.objects.filter(members__name__startswith=’Paul’)
  [<Group: The Beatles>]
  
  >>> Person.objects.filter(group__name=’The Beatles’, membership__date_joined__gt = date(1961,1,1))
  [<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
  u’Needed a new drummer
  
  >>> 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关系

        定义这种关系使用OneToOneField.需要一个固定参数:某个关系modelclass,这种关系可以理解成继承的关系,比如创建了一个’place’的数据库,里面含有address, phone等标准的信息,如果你再创建一个‘餐馆‘数据库,它是建立在’place’数据库之上的,因为‘餐馆’也需要一些地址信息,所以将‘餐馆’用OneToOneField定义到’place’之上。

 

跨文件的models

        可以引用来自另一个应用的model,只需要将另一个应用的model import进来,然后就可以使用了。见如下例子:

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

字段名称的限制

不能是python保留的关键字

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

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

当然,django也是允许我们自定义字段类型,可以查阅相关文档。

Meta Options

通过使用内部嵌套的class Meta可以给定modelmetadata,如下:

Class ox(models.Model):
  Horn_length = models.IntegerField()
    
  Class Meta:
      ordering = [‘horn_length’]
      verbose_name_plural = “oxen”

Meta的各种options有很多,可以查看相关资料。

Model methods

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

__unicode__()

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

 

get_absolute_url():告诉django怎样计算对象的url,django一般在它的admin接口上使用,无论何时admin都需要知道一个对象的url.

 

覆盖预定义的model 方法

即可以定制一些model的方法,经常要改变的是save()delete()工作。

当在保存一个对象时你希望要进行一些其它额外的操作时,可以改写成如下方式:

Class Blog(models.Model):
  #....
  def save(self, *args, **kwargs):
      do_something()
      super(Blog, self).save(*args, **kwargs) # call the real save()method
      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的字段会被添加到子类中。如下:

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不可以用作正常的Django model,因为它只是一个抽象基类,它并不生成一个数据库,不能直接实例化或直接保存数据。所以这种抽象基类是非常有用的,它以python的方式提供了一种方法来提取出公共信息,同时仅当子model在创建数据库时才会创建相应的数据。

 

Meta的继承

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

class CommonInfo(models.Model):
  name = models.CharField(max_length = 100)
  age = models.PositiveIntegerField()
  
  class Meta:
      abstract = True
  Ordering = [‘name’]
  
class Student(CommonInfo):
  home_group = models.CharField(max_length = 5)
  
  class Meta(CommonInfo.Meta):
      db_table = ‘student_info’

    Django的确对抽像基类中的Meta进行过一次修改,在子类进行安装Meta属性之前,它对abstract设置为false,这意味着子类不会自动的成为抽像基类,当然,如果我们自己想让子类成为抽像基类的话,可以显式地设置abstructtrue即可。

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

 

小心related_name属性参数

    若在ForeignKeyManyToManyField中使用related_name属性,必须让字段名称具有唯一性。否则将会引起一些问题,因为这个字段将会被包含到各个子类中,而每次此属性字段都具有相同的值。

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

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

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

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

class Base(models.Model):
  m2m=models.ManyToManyField
          (OtherModel, related_name = “%(app_label)s_%(class)s_related”)
    
  class Meta:
      Abstract = True

class ChildA(Base):
  Pass
class ChildB(Base):
  Pass

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

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

    这样,ChindA当中的m2m名称将是common_childa_related, 同时ChindB中的m2m名称为common_childb_related,而另一应用rare中的childB中的m2mrare_childb_related.这样可以保证唯一性。

    注意:如果不指定related_name属性,系统会有默认的名称,可以查看相关资料。


Multi-table inheritance

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

class Place(models.Model):
  name = models.CharField(max_length=50)
  address = models.CharField(max_length=80)
  
class Restaurant(Place):
  serves_hot_dogs = models.BooleanField()
  serves_pizza = models.BooleanField()

虽然数据保存在这两个不同的数据库表中,但Place中的所有字段在Restaurant中均可被访问,(也可以这么理解:Place主要存储所有类似于Restaurant各种商店的地址,Restaurant对象只能访问其本身在Place中存放的地址,而Place则可以访问所有商店的地址和名称),见如下例子:

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

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

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

#如果p是一个Restaurant对象,即p是一个子类
>>>.p.restaurant #请注意,是小写,这样可以直接访问Restaurant对象
<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,如下:

  class ChildModel( ParentModel):
      ...
      class Meta:
          #remove parent’s ording effect
          Ordering = []

Inheritance and reverse relations

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

Class Supplier(Place):
  #必须在所有的关系中说明related_name
  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添加一个方法,可以如下操作:

  class MyPerson(Person):
      class Meta:
          Proxy = True
  def do_something(self):
      ...

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

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

我们可以通过代理模型对原模型进行不同方式的排序,如下,可以增加last_name属性进行排序:

class OrderedPerson(Person):
  class Meta:
      ordering = [“last_name”]
      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时你可以改变它的默认管理器:

  class NewManager(models.Manager):
      ...
  class MyPerson(Peson):
      objects = NewManager()
      class Meta:
        Proxy = True 

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

  #为新管理器创建一个抽象类
  class ExtraManagers(models.Model):
      secondary = NewManager()
      class Meta:
          Abstract = True
  class MyPerson(Person, ExtraManagers): #紧接在Person之后
      class Meta:
           proxy = True;

    我们可能不会经常这样做,但是有时也有需要的时候,所以这里先了解一下。

 

关于Multiple inheritance

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

一般来说,很少会从多个父model中继承,主要的使用原则就是越简单越好,这样可以避免总是去寻找一些特殊的来自其它类中的信息。

 

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

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值