首页 > 其他分享 >Django学习小记[2] —— Model

Django学习小记[2] —— Model

时间:2023-08-27 10:35:22浏览次数:45  
标签:model name models Django Model NULL id 小记


开始学习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 models

class 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.name

class Group(models.Model):
    name = models.CharField(max_length=30)
    members = models.ManyToManyField(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_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 = True

class 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.name

class 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.name

class 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>]
>>>

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

标签:model,name,models,Django,Model,NULL,id,小记
From: https://blog.51cto.com/u_5173797/7251090

相关文章

  • Django学习小记[1] —— Start
    Part1Part1通过举例,从整体上过了一遍django的基本内容,包括project,app,database,model等内容。有几下内容需要注意:projectvs.appapp是一个web应用程序,它是实际用来做事的,比如zinnia这个用django写的博客引擎就是一个app,但是一个project是配置文件和app的集合,相当于一个......
  • 「Log」2023.8.26 小记
    序幕起晚了,干脆破罐子破摔,晚点到。八点前到校,被教练投喂雪糕。水两道红题,选笔记本巴拉巴拉。讲题讲题胡题。吃饭。讲题讲题胡题。摆。摆。摆。摆。摆。摆。摆。摆。摆。......
  • 【补充】Django中的信号
    【一】Django中的信号Django中的信号是一种机制,用于在特定事件发生时自动触发相关的操作或函数。通过使用信号,可以实现模块间的解耦和事件驱动的编程。在Django中,有两种类型的信号:内置信号和自定义信号。【二】内置信号Django提供了许多内置信号,以便我们在与数据库交互......
  • js_中文输入法情况下, 输入框v-model绑定值中没有输入值但却触发input事件的问题
    中文输入法情况下,输入框v-model绑定值中没有输入值但却触发input事件的问题今天写的一个搜索框,要求输入字符时不作处理,直到用户点击搜索按钮时才执行搜索逻辑;当用户将搜索框文本删除至空字符串时,执行一次无搜索值的搜索逻辑,用于将表格数据恢复至无筛选;在这个功能......
  • Kruskal重构树小记
    模拟赛考了,简单贺一下oi-wiki引入定义在跑\(\rmKruskal\)的过程中我们会从小到大加入若干条边。现在我们仍然按照这个顺序。首先新建\(n\)个集合,每个集合恰有一个节点,点权为\(0\)。每一次加边会合并两个集合,我们可以新建一个点,点权为加入边的边权,同时将两个集合的根节......
  • Django 中实现上传图片配置
    models文件创建的字段模型,类型为ImageField,在ImageField中添加以下代码(如果该文件夹不存在则自动创建) settings文件代码如下  url配置 ......
  • 「Log」2023.8.25 小记
    序幕到校同学都没来,先摆。写博客,写啊,写啊。改费用流板子。\(\color{royalblue}{P3381\【模板】最小费用最大流}\)板子。痛心疾首,建边的时候费用边反边为负权边。\(\text{Link}\)间幕\(1\)水一道平衡树加强版,直接复制粘贴板子,无意义的。网络流。\(\color{royalblue}{P......
  • django配置swagger自动生成接口文档以及自定义参数设置
    首先安装swagger所用的包pipinstalldrf-yasg然后再settings.py中注册app     接口采用的token认证,在settings.py配置认证方式SWAGGER_SETTINGS={'USE_SESSION_AUTH':False,'SECURITY_DEFINITIONS':{......
  • 创建第一个Django app-part5
    自动化测试开始第一个测试首先有一个bugpython3manage.pyshell创建一个测试来暴露这个bug将下面的代码写入polls应用里的tests.py文件内点击查看代码fromdjango.testimportTestCase#Createyourtestshere.importdatetimefromdjango.utilsimporttim......
  • 反演小记
    反演反演,可以理解为两个事物通过某种关系的互相转化。基本推论设\(F,G\),满足\(F(n)=\sumV[n,i]G(i)\),其中\(V\)为矩阵,将\(F,G\)看成列向量,可以写做\(F=V*G\),那么我们可以容易推出\(G=V^{-1}*F\),这就是反演的过程,而一些特殊的反演即是利用了\(V\)和\(V^{-1}......