Tuesday, October 27, 2009

django的ORM操作

不可否认, django的文档写的很好, 也很多文档.


对于本人还是那句话: 如果使用文字不能把一件事物清楚地描述出来的话, 那么你对这件事物还是不清晰 .  (^_^)


ORM类实例对象的创建

1. 使用类的__new__方法

2. 使用类Manager.create方法


两个类:

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_stars = models.IntegerField()


创建对象:

1. 使用类的__new__方法:

In [6]: m = Musician(first_name='firename', last_name='lastname', instrument="something")
In [7]: m.id

这时, 新创建的对象是没有id的. 也就是还没有数据库的记录. 还需要使用方法把数据存在到数据库中:

In [8]: m.save()
In [9]: m.id
Out[9]: 2L

2. 使用类的Manager.creater方法:

In [4]: m = Musician.objects.create(first_name='firename', last_name='lastname', instrument="something")
In [5]: m.id
Out[5]: 3L

这时, 数据已经被添加到数据库表, 不用调用save方法


任何model类实例对象都有这两种方法创建.

ManyToOne Field添加引用实例

Album与Mussician的关系是"多对一", Album创建实例时多了一个条件: Album的__new__和Manager.create会检查artist参数是不是Musician的实例:

__new__方法:

In [12]: a = Album(artist=2, name='album name', release_date=datetime.datetime.now(), num_stars=10)

ValueError: Cannot assign "2": "Album.artist" must be a "Musician" instance.

Manager.create方法:

In [13]: a = Album.objects.create(artist=2, name='album name', release_date=datetime.datetime.now(), num_stars=10)
ValueError: Cannot assign "2": "Album.artist" must be a "Musician" instance.

* 传个与primary key相同的数字还是出错

需要在artist参数位置传入一个指定的实例:

步骤1: 使用Musician的__new__或者Manager.create创建Musician对象:

m = Musician(first_name='firename', last_name='lastname', instrument="something")

m.save() # 一定要被存在在数据库中才能被Album的创建方法正常使用, 则否出错.

a = Album(artist=m, name='album name', release_date=datetime.datetime.now(), num_stars=10)

a.save()


如果m不使用save()的情况:

In [27]: m = Musician(first_name='firename', last_name='lastname', instrument="something")
In [28]: a = Album(artist=m, name='album name', release_date=datetime.datetime.now(), num_stars=10)
In [29]: a.save()
IntegrityError: (1048, "Column 'artist_id' cannot be null")

出错. 说artist_id为null


使用对象:

有两个方向: 1. 从Foreign端; 2. 从被Foreign端

1. 从Foreign端

In [19]: a = Album.objects.get(pk=1)
In [20]: a.artist.id
Out[20]: 4L

In [22]: a.artist.first_name
Out[22]: u'firename'
这种引用方法很pythonic


2. 从被Foreign端


这时候被称为: The “other side” of a ForeignKey relation. 如果一个model类被其它类通过foreign key引用的话. 那么, 在被引用

的类和这个类的实例中, 会多一个属性:

In [61]: type Musician.album_set
-------> type(Musician.album_set )
Out[61]: <class 'django.db.models.fields.related.ForeignRelatedObjectsDescriptor'>
In [62]: m = Musician.objects.get(pk=1)
In [63]: type m.album_set
-------> type(m.album_set )
Out[63]: <class 'django.db.models.fields.related.RelatedManager'>
在django文档中有这样一句话:

Following relationships "backward"


If a model has a ForeignKey, instances of the foreign-key model will have
access to a Manager that returns all instances of the first model. By
default, this Manager is named FOO_set, where FOO is the source
model name, lowercased. This Manager returns QuerySets, which can be
filtered and manipulated as described in the "Retrieving objects"


作用就是能通过此方法找到一个对象都被哪些对象引用


# 下面是从album实例找到引用的musician实例

In [64]: a = Album.objects.get(pk=1)
In [65]: a.id
Out[65]: 1L

In [67]: a.artist.id
Out[67]: 4L

# 下面是从musician实例找到被哪个album实例引用了

In [68]: m = Musician.objects.get(pk=4)
In [70]: a_set = m.album_set.all()
In [71]: a_set.count()
Out[71]: 1
In [72]: a = a_set[0]
In [73]: a.id
Out[73]: 1L

蓝色的部分都是一样的, 表示是同一个album实例

ManyToMany Field添加引用实例

class Domain(models.Model):
    name = models.CharField(max_length=128)
    is_singular = models.IntegerField()
    description = models.CharField(max_length=32, default="")
    object = models.ManyToManyField("Object")

class Object(models.Model):
    name = models.CharField(max_length=128)
    description = models.CharField(max_length=32, default="")

ManyToMany字段是可以引用(foreign)多个实例的. 这通过ManyToMany Field的add方法实例:
 In [2]: domain = Domain(name = 'china', is_singular=1)
In [3]: domain.object.add
ValueError: 'Domain' instance needs to have a primary key value before a many-to-many relationship can be used.
In [5]: domain.object.add
Out[5]: <bound method ManyRelatedManager.add of <django.db.models.fields.related.ManyRelatedManager object at 0x9fa82ec>>
可以看出, ManyToMany Field的add方法是需要在save后才有的.

下面为ManyToMany Field增加引用实例:
In [6]: obj = Mod.Object(name="object1")
In [7]: domain.object.add(obj)
IntegrityError: (1048, "Column 'object_id' cannot be null")
* 与Foreign Field是一样的, 都是需要被引用的对象先存在于数据库表中!
In [8]: obj.save()
In [9]: domain.object.add(obj)
In [10]: domain.save()

* 调用obj.save后, 才成功添加引用实例!
还是可以引用多个实例的:
In [11]: obj = Object(name="object2")
In [12]: obj.save()
In [13]: domain.object.add(obj)
In [14]: domain.save()
* 这么多个save(), 是因为底层还是关系型数据库, 每一次save就对应着一次insert或者是update

通过through参数, 使用定制义的中间表, 此后, ManyToMany Field没有了add方法!

新的模型如下:
class Domain(models.Model):
    name = models.CharField(max_length=128)
    is_singular = models.IntegerField()
    description = models.CharField(max_length=32, default="")
    object = models.ManyToManyField("Object", through="DomainObject")

class Object(models.Model):
    name = models.CharField(max_length=128)
    description = models.CharField(max_length=32, default="")

class DomainObject(models.Model):
    domain = models.ForeignKey("Domain")
    object = models.ForeignKey("Object")

# 找一找ManyToMany Field的add方法:
In [2]: domain = Mod.Domain(name = 'china', is_singular=1)
In [3]: domain.save()
In [4]: domain.add
AttributeError: 'Domain' object has no attribute 'add'
* 被掩了!
为ManyToMany Field添加引用实例需要使用如下方法:
In [6]: domain
Out[6]: <Domain: Domain object>
In [7]: obj
Out[7]: <Object: Object object>
In [8]: domain_object = Mod.DomainObject.objects.create(domain=domain, object=obj)
* 这里的Manager.create方法可以类的__new__方法
这是显式使用了中间表对应的类来生成映射关系.

使用对象:

和上面的ManyToOne情况一样, 有两个方向: 1. 从Foreign端; 2. 从被Foreign端

1. 从Foreign端
In [23]: domain.object
Out[23]: <django.db.models.fields.related.ManyRelatedManager object at 0x9fa802c>
为一个Manager对象!
In [24]: objs = domain.object.all()
In [27]: for item in objs:
   ....:     print item.id
1
2
可见, 这时间是需要指定select条件才能取回自己想要的对象

2. 从被Foreign端
In [28]: obj = domain.object.all()[0]
In [29]: obj.id
Out[29]: 1L
In [30]: domain.id
Out[30]: 1L
可见, ID为1的domain引用了ID为1的object, 反过来找:
In [35]: obj = Object.objects.get(pk=1)
In [39]: domain = obj.domain_set.get(pk=1)
In [40]: domain.id
Out[40]: 1L

改变RelatedManager的名字

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, related_name="Musician_set")
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

多了红色代码, 然后:
In [7]: m = Musician(first_name='first_name', last_name='last_name', instrument='instrument')
In [8]: m.Musician_set
Out[8]: <django.db.models.fields.related.RelatedManager object at 0x8de220c>


No comments:

Post a Comment

Note: Only a member of this blog may post a comment.