Django学习小记[2] —— Model

来源:互联网 发布:重庆正大软件职业学院 编辑:程序博客网 时间:2024/05/27 03:25

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

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

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

from django.db import modelsclass Person(models.Model):    first_name = models.CharField(max_length=30)    last_name = models.CharField(max_length=30)

所有的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来表示,就是:

class Manufacturer(models.Model):    name = models.CharField(max_length=30)class Car(models.Model):    Manufacturer = models.ForeignKey(Manufacturer)    name = models.CharField(max_length=30)

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

CREATE TABLE `model_test_manufacturer` (    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,    `name` varchar(30) NOT NULL);CREATE TABLE `model_test_car` (    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,    `Manufacturer_id` integer NOT NULL,    `name` varchar(30) NOT NULL);ALTER TABLE `model_test_car` ADD CONSTRAINT `Manufacturer_id_refs_id_da7168cb` FOREIGN KEY (`Manufacturer_id`) REFERENCES `model_test_manufacturer` (`id`);

增删操作:

>>> from model_test.models import Car>>> from model_test.models import Manufacturer>>> m = Manufacturer.objects.get(name="xxx")>>> m.car_set.all()[]>>> m.car_set.create(name="yyy")<Car: Car object>>>> c = Car(name="zzz")>>> m.car_set.add(c)

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

多对多

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

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

class Person(models.Model):                            name = models.CharField(max_length=30)    def __unicode__(self):                                 return self.nameclass Group(models.Model):    name = models.CharField(max_length=30)    members = models.ManyToManyField(Person, through='Membership')    def __unicode__(self):        return self.nameclass Membership(models.Model):    person = models.ForeignKey(Person)    group = models.ForeignKey(Group)    date_joined = models.DateField()    invite_person = models.CharField(max_length=30)

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

CREATE TABLE `model_test_person` (    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,    `name` varchar(30) NOT NULL);CREATE TABLE `model_test_group` (    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,    `name` varchar(30) NOT NULL);CREATE TABLE `model_test_membership` (    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,    `person_id` integer NOT NULL,    `group_id` integer NOT NULL,    `date_joined` date NOT NULL,    `invite_person` varchar(30) NOT NULL);ALTER TABLE `model_test_membership` ADD CONSTRAINT `group_id_refs_id_be33a6a7` FOREIGN KEY (`group_id`) REFERENCES `model_test_group` (`id`);ALTER TABLE `model_test_membership` ADD CONSTRAINT `person_id_refs_id_90aaf3d5` FOREIGN KEY (`person_id`) REFERENCES `model_test_person` (`id`);

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

>>> import datetime>>> from model_test.models import Person>>> from model_test.models import Group>>> from model_test.models import Membership>>> >>> suo = Person.objects.create(name="suo")>>> piao = Person.objects.create(name="piao")>>> english = Group.objects.create(name="English")>>> >>> m1 = Membership(person=suo, group=english, date_joined=datetime.date(2014, 9, 9), invite_person="summer")           >>> m1.save()>>> m2 = Membership.objects.create(person=piao, group=english, date_joined=datetime.date(2014, 8, 8), invite_person="spring")>>> >>> english.members.all()[<Person: suo>, <Person: piao>]

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

english.members.add(suo)

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

一对一

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

class IdCard(models.Model):    number = models.CharField(max_length=30)    person = models.OneToOneField(Person)class Person(models.Model):    name = models.CharField(max_length=30)    def __unicode__(self):        return self.name

对应的SQL语句为:

CREATE TABLE `model_test_person` (    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,    `name` varchar(30) NOT NULL);CREATE TABLE `model_test_idcard` (    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,    `number` varchar(30) NOT NULL,    `person_id` integer NOT NULL UNIQUE);ALTER TABLE `model_test_idcard` ADD CONSTRAINT `person_id_refs_id_c2c57084` FOREIGN KEY (`person_id`) REFERENCES `model_test_person` (`id`);

注意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,那么要显示的再次指定该参数。如下面的例子:

class CommonInfo(models.Model):    name = models.CharField(max_length=30)    age = models.PositiveIntegerField()    class Meta:        abstract = Trueclass Student(CommonInfo):    score = models.IntegerField()class Teacher(CommonInfo):    rating = models.CharField(max_length=30)

对应的SQL语句为:

CREATE TABLE `model_test_student` (    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,    `name` varchar(30) NOT NULL,    `age` integer UNSIGNED NOT NULL,    `score` integer NOT NULL);CREATE TABLE `model_test_teacher` (    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,    `name` varchar(30) NOT NULL,    `age` integer UNSIGNED NOT NULL,    `rating` varchar(30) NOT NULL);

Multi-table inheritance

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

class Place(models.Model):    name = models.CharField(max_length=30)    address = models.CharField(max_length=30)    def __unicode__(self):        return self.nameclass Restaurant(Place):    serves_hot_dogs = models.BooleanField()    serves_pizza = models.BooleanField()

得到的SQL如下:

CREATE TABLE `model_test_place` (    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,    `name` varchar(30) NOT NULL,    `address` varchar(30) NOT NULL);CREATE TABLE `model_test_restaurant` (    `place_ptr_id` integer NOT NULL PRIMARY KEY,    `serves_hot_dogs` bool NOT NULL,    `serves_pizza` bool NOT NULL);ALTER TABLE `model_test_restaurant` ADD CONSTRAINT `place_ptr_id_refs_id_cc7b5838` FOREIGN KEY (`place_ptr_id`) REFERENCES `model_test_place` (`id`);

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

>>> from model_test.models import Restaurant>>> Restaurant.objects.create(name="r1", address="a1", serves_hot_dogs=True, serves_pizza=False)<Restaurant: r1>

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

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

Proxy models

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

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

class Person(models.Model):    first_name = models.CharField(max_length=30)    last_name = models.CharField(max_length=30)    class Meta:        ordering = ['first_name']    def __unicode__(self):        return self.nameclass MyPerson(Person):    class Meta:        proxy = True        ordering = ['last_name']    def do_something(self):        pass

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

>>> from model_test.models import Person>>> from model_test.models import MyPerson>>> Person.objects.create(first_name="suo", last_name="guangyu")<Person: suo guangyu>>>> Person.objects.create(first_name="xing", last_name="demo")<Person: xing demo>>>> Person.objects.all()[<Person: suo guangyu>, <Person: xing demo>]>>> MyPerson.objects.all()[<MyPerson: xing demo>, <MyPerson: suo guangyu>]>>>

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

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 镇魔曲以前的角色怎么办 镇魔曲手游转职后装备怎么办 镇魔曲个性标签任务怎么办 教师资格证申请表打不开怎么办 怎么办appstore换到日本 电脑内存太小怎么办 win10声音卡顿怎么办 录音播放卡顿怎么办 笔记本电脑玩dnf卡怎么办 龙之谷约惠码被删了怎么办 天涯明月刀马没有了怎么办 icloud照片无法同步怎么办 ipad不能下载app怎么办 ipad屏幕孔进水怎么办 ipad无法验证登录怎么办 dnf电脑配置低怎么办 淘宝直播粉丝不够怎么办 电脑页面显示不全怎么办 脸上发痒长痘怎么办 扣扣魔性表情泡泡消失怎么办 脚起小泡泡很痒怎么办 孕妇脚痒起水泡怎么办 孕妇手脚起湿疹怎么办 嘴巴破皮了怎么办 小便刺痛阴唇红肿怎么办 集成墙面挂照片怎么办 苹果电脑网页游戏打不开怎么办 苹果6plus发热怎么办 玩手游手机太卡怎么办 苹果七发烫厉害怎么办 苹果手机延迟高怎么办 王者荣耀总是卡怎么办 王者荣耀卡屏怎么办 王者荣耀网络延迟怎么办 荣耀8手机卡了怎么办 荣耀v10有点卡怎么办 荣耀10有点卡怎么办 8g内存吃鸡会崩怎么办 玩看门狗很卡怎么办 拼多多人数不够怎么办 玩cf想吐怎么办