Django-4-模型

一、模型

模型提供了一种描述字段与数据库关系的模型。每一个模型都会映射一张数据库表。

  • 每个模型都是一个 Python 的类,这些类继承 django.db.models.Model

  • 模型类的每个属性都相当于一个数据库的字段。

  • 利用这些,Django 提供了一个自动生成访问数据库的 AP

1.1 属性命名限制

  • 遵循标识符规则

  • 由于django的查询方式,不允许使用连续的下划线

  • 定义属性时,需要字段类型,字段类型被定义在django.db.models.fields目录下,为了方便使用被导入到django.db.models中

1.2 使用方式

导入from diango.db import models
通过models.Field创建字段类型的对象,赋值给属性

1.3 逻辑删除和物理删除

对于重要数据都做逻辑删除,不做物理删除,实现方法是定义is_delete属性,类型为BooleanField默认值为False

is_delete = models.BooleanField(default=False)

1.4 常用字段选项

1.4.1 null

Field.null
如果是 True, Django 将在数据库中存储空值为 NULL。默认为 False
避免在基于字符串的字段上使用 null,如 CharField 和 TextField。如果一个基于字符串的字段有 null=True,这意味着它有两种可能的“无数据”值。NULL,和空字符串。在大多数情况下,“无数据”有两种可能的值是多余的,Django 的惯例是使用空字符串,而不是 NULL。一个例外是当一个 CharField 同时设置了 unique=Trueblank=True。在这种情况下,null=True 是需要的,以避免在保存具有空白值的多个对象时违反唯一约束。

1.4.2 blank

Field.blank
如果是 True ,该字段允许为空。默认为 False
注意,这与 null 不同。 null 纯属数据库相关,而 blank 则与验证相关。如果一个字段有 blank=True,表单验证将允许输入一个空值。如果一个字段有 blank=False,则该字段为必填字段。

1.4.3 choices

Field.choices
一个 sequence 本身由正好两个项目的迭代项组成(例如 [(A,B),(A,B)…] ),作为该字段的选择。如果给定了选择,它们会被 模型验证 强制执行,默认的表单部件将是一个带有这些选择的选择框,而不是标准的文本字段。

from django.db import models


class Student(models.Model):
    FRESHMAN = "FR"
    SOPHOMORE = "SO"
    JUNIOR = "JR"
    SENIOR = "SR"
    GRADUATE = "GR"
    YEAR_IN_SCHOOL_CHOICES = [
        (FRESHMAN, "Freshman"),
        (SOPHOMORE, "Sophomore"),
        (JUNIOR, "Junior"),
        (SENIOR, "Senior"),
        (GRADUATE, "Graduate"),
    ]
    year_in_school = models.CharField(
        max_length=2,
        choices=YEAR_IN_SCHOOL_CHOICES,
        default=FRESHMAN,
    )

    def is_upperclass(self):
        return self.year_in_school in {self.JUNIOR, self.SENIOR}

1.4.4 db_column

Field.db_column
这个字段要使用的数据库列名。如果没有给出列名,Django 将使用字段名。
如果你的数据库列名是 SQL 的保留字,或者包含了 Python 变量名中不允许的字符特别是连字符那也没关系。Django 会在幕后引用列名和表名。

1.4.5 db_index

Field.db_index
如果是 True,将为该字段创建数据库索引。

1.4.6 db_tablespace

Field.db_tablespace
如果这个字段有索引,那么要为这个字段的索引使用的 数据库表空间 的名称。默认是项目的 DEFAULT_INDEX_TABLESPACE 设置(如果有设置),或者是模型的 db_tablespace (如果有)。如果后端不支持索引的表空间,则忽略此选项。

1.4.7 default

Field.default
该字段的默认值。可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。
默认值不能是一个可更改的对象(模型实例、listset 等),因为对该对象同一实例的引用将被用作所有新模型实例的缺省值。相反,将所需的默认值包裹在一个可调用对象中。例如,如果你想为 JSONField 指定一个默认的 dict,使用一个函数:

def contact_default():
    return {"email""to1@example.com"}


contact_info = JSONField("ContactInfo", default=contact_default)

lambda 不能用于 default 等字段选项,因为它们不能被 迁移序列化。其他注意事项见该文档。
对于像 ForeignKey 这样映射到模型实例的字段,默认应该是它们引用的字段的值(默认是 pk 除非 to_field 被设置了),而不是模型实例。
当创建新的模型实例且没有为该字段提供值时,使用默认值。当字段是主键时,当字段设置为None 时,也使用默认值。

1.4.8 editable

Field.editable
如果是 False,该字段将不会在管理或任何其他 ModelForm 中显示。在 模型验证 中也会跳过。默认为 True

1.4.9 error_messages

Field.error_messages
error_messages 参数可以让你覆盖该字段引发的默认消息。传入一个与你想覆盖的错误信息相匹配的键值的字典。

1.4.10 primary_key

Field.primary_key
如果设置为 True ,将该字段设置为该模型的主键。
如果你没有为模型中的任何字段指定 primary_key=True,Django 会自动添加一个字段来保存主键,所以你不需要在任何字段上设置 primary_key=True,除非你想覆盖默认主键行为。自动创建的主键字段的类型可以在 AppConfig.default_auto_field 中为每个应用程序指定,或者在 DEFAULT_AUTO_FIELD 配置中全局指定。

1.5 常用字段类型

1.5.1 AutoField

根据可用的 ID 自动递增。你通常不需要直接使用它;如果你没有指定,主键字段会自动添加到你的模型中.

1.5.2 BigAutoField

一个 64 位整数,与 AutoField 很相似,但保证适合 19223372036854775807 的数字

1.5.3 BigIntegerField

一个 64 位的整数,和 IntegerField 很像,只是它保证适合从 -92233720368547758089223372036854775807 的数字

1.5.4 BooleanField

一个 true/false 字段。
该字段的默认表单部件是 CheckboxInput,或者如果 null=True 则是 NullBooleanSelect。
当 Field.default 没有定义时,BooleanField 的默认值是 None

1.5.5 CharField

一个字符串字段,适用于小到大的字符串。
对于大量的文本,使用 TextField。

1.6 案例 创建模型并迁移

from django.db import models


class StudentModel(models.Model):
    # 自定义主键为sid,默认id就不自动创建
    sid = models.AutoField(auto_created=True, primary_key=True)
    # 名字唯一,长度最大50,添加索引
    name = models.CharField(max_length=50, unique=True, db_index=True)
    # 名字,默认20
    age = models.IntegerField(default=20)
    # bool 类型
    sex = models.BooleanField(default=True)
    # null=true可以为空;black=true代表可以在admin管理页面为空
    info = models.TextField(null=True, blank=True)
    # 小数,最大长度4 小数点后2为
    money = models.DecimalField(max_digits=4, decimal_places=2, default=10.88)

    # 日期
    birthday = models.DateField(default='2023-01-01')
    # 每次修改就会修改改时间
    birthday2 = models.DateTimeField(auto_now=True)
    # 新增时增加该时间
    birthday3 = models.DateTimeField(auto_now_add=True)

当修改了模型信息时,会出现如下内容:让你选择,可以给每一个字段都增加一个默认值,这样的话出现如下选择时就可以根据1来进行选择了。

Django-4-模型
image.png

二、单表操作

ORM关系

模型      表
类结构    表结构
对象      表的一条数据
类属性    表的字段  

创建模型并迁移

class PersonModel(models.Model):
    name = models.CharField(max_length=30, unique=True)
    age = models.IntegerField(default=20)

    # 元数据
    class Meta:
        # 指定生成的表名字
        db_table = 'tb_person'

2.1 新增

2.1.1 save()

def add_per(request):
    p = PersonModel()
    p.name = 'zhangsan'
    p.age = 22
    p.save()  # 保存到数据库

    return HttpResponse('add success')
def add_per(request):
    p = PersonModel(name = 'zhangsan',age = 22)

    p.save()  # 保存到数据库
    return HttpResponse('add success')
def add_per(request):
    try:
        p = PersonModel()
        p.name = 'zhangsan'
        p.age = 22
        p.save()  # 保存到数据库
    except Exception as e:
        return HttpResponse("add failed")

    return HttpResponse('add success')

2.1.2 create()

def add_per(request):
    p = PersonModel(name = 'zhangsan',age = 22)

    PersonModel.objects.create(p)  # 保存到数据库
    return HttpResponse('add success')

2.1.3 `get_or_create`

可以放值重复

def add_per(request):
    p = PersonModel(name = 'zhangsan',age = 22)

    PersonModel.get_or_create(p)  # 保存到数据库
    return HttpResponse('add success')

2.2 删除

删除,要先查询到,然后再删除

def del_per(request):
    try:
        # 删除数据,先得到再删除
        p = PersonModel.objects.first()
        p.delete()
    except Exception as e:
        return HttpResponse('删除失败')

    return HttpResponse('删除成功')

2.4 修改

先查询到,然后修改,然后保存

def upd_per(request):
    try:

        p = PersonModel.objects.first()
        p.age = 33
        p.save()
    except Exception as e:
        return HttpResponse('修改失败')

    return HttpResponse('修改成功')

由于是先查询,后更新,所以,有可能有一些没有变化但是也更新了;
可以指定只更新哪些字段

def upd_per(request):
    try:

        p = PersonModel.objects.first()
        p.age = 33
        p.save(update_fields=['age'])
    except Exception as e:
        return HttpResponse('修改失败')

    return HttpResponse('修改成功')

2.5 查询

2.5.1 `get()`:获取单条数据

PersonModel.objects.get(id=123)
  • 如果没有找到符合条件的对象,会引发模型类.DoesNotExist异常

  • 如果找到多个,会引发模型类.MultipleObjectsReturned 异常

2.5.2 first()

返回查询集(QuerySet)中的第一个对象

2.5.3 last()

返回查询集中的最后一个对象

2.5.4 count()

返回当前查询集中的对象个数

2.5.5 exists()

判断查询集中是否有数据,如果有数据返回True没有反之

2.5.6 all()

获取全部数据

PersonModel.objects.all()

2.5.7 values()

获取指定列的值,可以传多个参数!返回包含字典的列表(保存了字段名和对应的值)

PersonModel.objects.al1().values('password')

2.5.8 values list()

获取指定列的值,可以传多个参数!返回包含元组列表 (只保存值)

PersonModel.objects.all().values list('password')

2.5.9 获取个数

PersonModel.objects.filter(name='张三').count()

2.5.10 filter条件过滤

# 获取id大于1的值
PersonModel.objects.filter(id__gt=1)
# select * from Author where id > 1
#获取id大于或等于1的值
PersonModel.objects.filter(id__gte=1)
# select * from Author where id >= 1
# 获取id小于1@的值
PersonModel.objects.filter(id_lt=10)
# select * from Author where id < 10
# 获取id小于或等于1的值
PersonModel.objects.filter(id__lte=10)
# select * from Author where id <= 10
 # 获取id大于1 且 小于10的值
PersonModel.objects.filter(id__lt=10, id__gt=1)
# select * from Author where id < 1 and id > 1
 # 获取id在11、22、33中的数据
PersonModel.objects.filter(id__in=[112233])
# select * from Author where id in (11,22,33)
# not in
PersonModel.objects.exclude(id__in=[112233]) # select * from Author where id not in (11,22,33)
 # contains (和数据库中like语法相同)
PersonModel.objects.filter(name__contains="ven")
# select * from Author where name like %ven%!
PersonModel.objects.filter(name__icontains="ven")
# icontains大小写不敏感
PersonModel.objects.filter(name__regex="^ven"# 正则匹配
PersonModel.objects.filter(name_iregex="^ven")# 正则匹配,忽略大小写
PersonModel.objects.filter(age_range=[1020]) # 范围bettwen and
# startswith, istartswith, endswith, iendswith:
# 以什么开始,以什么结束,和上面一样带i的是大小写不敏感的, 其实不带i的也忽略大小写
PersonModel.objects.filter(name='seven').order_by('id'# asc升序
PersonModel.objects.filter(name='seven').order_by('-id')  # desc降序
PersonModel.objects.all()[10:20]
# 切片,取所有数据的10条到20条,分页的时候用的到

2.6 手动分页

page页码
per_page每页数量=5
第1页 (page=1):0-4 =>[:5]
第2页(page=2):5-9=>[5:10]
第3页(page=3):10-14=>[10:15]
第4页(page=4): 15-19=>[15:20]
得到每页数据

[(page-1)*per_page: page*per_page]

2.6 聚合

使用aggregte()函数返回聚合函数的值

  • Avg:平均值

  • Count:数量

  • Max:最大

  • Min: 最小

  • Sum:求和

from django.db.models import Count,Min,Max,Sum

PersonModel.objects.aggregate(Max('age'))

三、案例

3.1 先增加一下数据

视图函数中增加如下:

def add_per(request):
    for i in range(10):
        p = PersonModel()
        p.name = 'zhangsan' + str(i)
        p.age = 22 + i
        p.save()  # 保存到数据库

    return HttpResponse("ok")
Django-4-模型
image.png

3.2 查询

3.2.1 使用get

def get_one_person(request):
    p = PersonModel.objects.get(id=1)
    print(p, type(p))  # PersonModel object (1) <class 'student.models.PersonModel'>
    print(p.name, p.age)  # zhangsan0 22
    return HttpResponse("ok")

get如果找不到,就不会报DoesNotExist异常。
如果找到多个,就会报MultipledObjectsReturned异常。
get使用时,不能直接使用参数, 必须指定所用属性

get(id=1# 可以调用
get(1# 失败
get(pk=18# 可以,使用主键

3.2.2 all

def get_all_person(request):
    persons = PersonModel.objects.all()
    print(persons)  # <QuerySet [<PersonModel: PersonModel object (1)>, <PersonModel: PersonModel object (2)>, <PersonModel: PersonModel object (3)>, <PersonModel: PersonModel object (4)>, <PersonModel: PersonModel object (5)>, <PersonModel: PersonModel object (6)>, <PersonModel: PersonModel object (7)>, <PersonModel: PersonModel object (8)>, <PersonModel: PersonModel object (9)>, <PersonModel: PersonModel object (10)>]>
    print(type(persons))  # <class 'django.db.models.query.QuerySet'>
    for p in persons:
        print(p.name, p.age)
    return HttpResponse("ok")

3.2.3 filter

def get_filter_person(request):
    persons = PersonModel.objects.filter(age__gte=23)
    print(
        persons)  # <QuerySet [<PersonModel: PersonModel object (1)>, <PersonModel: PersonModel object (2)>, <PersonModel: PersonModel object (3)>, <PersonModel: PersonModel object (4)>, <PersonModel: PersonModel object (5)>, <PersonModel: PersonModel object (6)>, <PersonModel: PersonModel object (7)>, <PersonModel: PersonModel object (8)>, <PersonModel: PersonModel object (9)>, <PersonModel: PersonModel object (10)>]>
    print(type(persons))  # <class 'django.db.models.query.QuerySet'>
    for p in persons:
        print(p.name, p.age)
    return HttpResponse("ok")

查询集QuerySet可以继续链式调用

persons = PersonModel.objects.filter(age__gte=23)
persons.first() # 取值第一个
person.last() # 取值最后一个
person.count() # 总数

3.2.4 聚合

PersonModel.objects.aggregate((Max('age')))

3.3 分页

def pageInfo(request, page=1):
    per_page = 10
    all_data = PersonModel.objects.all()

    # 分页器
    paginator = Paginator(all_data, per_page)
    persons = paginator.page(page)  # 获取page页数据
    pages = paginator.page_range  # 页码范围,可以遍历

    return render(request, 'page.html', {
        'persons': persons,
        'pages': pages
    })

四、配置MySQL

4.1 MySQL驱动

使用mysqlclient

pip install mysqlclient

pip install -i https://pypi.douban.com/simple mysqlclient
# Linux Ubuntu下需要先安装
apt install libmysqld-dev

4.2 配置

在Django中配置和使用mysql数据库。
使用mysql数据库,settings中配置如下

DATABASES ={
    'default': (
        'ENGINE':'django.db.backends.mysql',
        'NAME':'mydb',
        'USER':'root',
        'PASSWORD':'123456',
        'HOST':'127.0.0.1',
        'PORT':'3306'
    }
}

五、多表关联

5.1 关联关系

  • ForeignKey:一对多,将字段定义在多的端中

  • ManyToManyField:多对多,将字段定义在两端的任意一端中

  • OneToOneField:一对一,将字段定义在任意一端中

5.1.1 一对多关系

举例说明 (一对一, 多对多类似) :
一个班级可以有多个学生, 一个学生只能属于一个班级

class Grade(models.Model):
    name = models.CharField(max length=20)


class Student(models.Model):
    name = models.CharField(max length=20)
    grade = models.ForeignKey(Grade)

对象的使用:
正向 (在student这边,有grade属性的这一边)

  • 获取学生所在班级(对象):stu.grade

  • 获取学生所在班级的属性: stu.grade.name

反向(在Grade这边) :

  • 获取班级的所有学生(获取Manager对象): grade.student_set

  • 获取班级的所有学生(取QuerySet查询集): grade.student_set.all()

filter(),get()等操作中的使用:
正向(在student这边,有grade属性的这一边):

  • Student.objects.filter(属性__name="1')

  • 如:Student.objects.filter(grade__name='1')

反向(在Grade这边)

  • Grade.objects.filter(类名小写__id=7)

  • 如: Grade.objects.filter(student__id=7)

六、Model连表结构

一对多:models.ForeignKey(其他表)
多对多: models.ManyToManyField(其他表)
一对一: models.OneToOneField(其他表)

6.1 一对多关联

一对多关系,即外键。为什么要用一对多。先来看一个例子。有一个用户信息表,其中有个用户类型字段,存储用户的用户类型。如下

class UserInfo(models.Model):
    username = models.CharField(max length=32)
    age = models.IntegerField()
    user_type = models.CharField(max length=10)

不使用外键时用户类型存储在每一行数据中。如使用外键则只需要存储关联表的id即可,能够节省大量的存储空。同时使用外键有利于维持数据完整性和一致性。
当然也有缺点,数据库设计变的更复杂了。每次做DELETE 或者UPDATE都必须考虑外键约束。
刚才的例子使用外键的情况: 单独定义一个用户类型表:

class UserType(models.Model):
    caption = models.CharField(max length=32)

class UserInfo(models.Model):
    user_type = models.ForeignKey('UserType')
    username = models.CharField(max length=32)
    age = models.IntegerField()

我们约定:
**正向操作: ****ForeignKey在UserInfo表里,如果根据UserInfo去操作就是正向操作**
**反向操作: ****ForeignKey不在UserType里,如果根据UserType去操作就是反向操作**

6.1.1 正向操作

增加

1)创建对象实例,然后调用save方法:

obj= UserInfo(name='li',age=44,user_type_id=2)
obj.save()

2)使用create方法

UserInfo.objects.create(name='li',age=44,user_type_id=2)

3)使用get_or_create方法,可以防止重复

UserInfo.objects.get_or_create(name='li',age=55,user_type_id=2)

4)使用字典

dic = (
    'name':'zhangsan',
    'age':18
    'user_type_id':3
)

UserInfo.objects.create(**dic)

5)通过对象添加

usertype = UserType.objects.get(id=1)
UserInfo.objects.create(name='li',age=55,user_type=usertype)

删除

和普通模式一样,先查询后删除

UserInfo.objects.filter(id=1).delete()

修改

和普通模式一样,先查询后修改

UserInfo.objects.filter(id=1).update(user_type_id=4)

查找

users = UserInfo.objects.filter(user_type__caption__contains='钻石')

正向获取关联表中的属性可以直接使用点.语法,比如:
获取users查询集中第一个用户的caption:

users[0].user_type.caption

6.1.2 反向操作

增加

通过usertype来创建userinfo

1) 通过userinfo set的create方法

#获取usertype实例
ut = UserType.objects.get(id=2)#创建userinfo
ut.userinfo_set.create(name= 'smith',age=33)

删除

删除操作可以在定义外键关系的时候,通过on_delete参数来配置删除时做的操作。on_delete参数主要有以下几个可选值:

  • models.CASCADE 默认值(Django1.11),表示级联删除,即删除UserType时相关联的UserInfo也会被删除。

  • models.PROTECT保护模式, 阻止级联删除。

  • models.SET_NULL 置空模式,设为null,null=True参数必须具备

  • models.SET_DEFAULT 置默认值 设为默认值,default参数必须具备

  • models.SET()删除的时候重新动态指向一个实体访问对应元素,可传函数

  • models.DO NOTHING  什么也不做。 (Django1.11)

注意:修改on_delete参数后,需要重新同步数据库。

修改

和普通模型一样,不会影响级联表。

查询

通过usertype对象来查用户类型为1的用户有哪些

obj=UserType.objects.get(id=1)
obj.userinfo_set.all()

可以通过在定义foreignkey时指定related_name来修改默认的userinfo_set
比如指定related_nameinfo

user_type = models.ForeignKey("UserType",related_name= 'info')

指定related_name之后,反向查的时候就变成了obj.info.all()获取用户类型为1且用户名为shuaige的用户

obj.info.filter(username='shuaige')

外键关系中,django自动给usertype加了一个叫做userinfo的属性。使用双下划线可以通过userinfo提供的信息来查usertype (了解)

user_type_obj = UserType.objects.get(userinfo__username='zs')

6.2 多对多关联

针对多对多关系django会自动创建第三张表。也可以通过through参数指定第三张表。用户和组是典型的多对多关系:

class Group(models.Model):
    name = models.CharField(max length=20)

      def __str__(self):
          return self.name


class User(models.Model):
    name = models.CharField(max length=64)
    password = models.CharField(max length=64)
    groups = models.ManyToManyField(Group)

    def _str__(self):
        return self.name

6.2.1 新增

先分别创建user和group,再使用add关联

u = User(name='aa',password='123')
u.save()

g = Group(name='g5')
g.save()

通过Manager对象使用add()方法
u.groups.add(g) 或 g.user_set.add(u)

6.2.2 删除

和一对多类似,删除user或group会级联删除user_groups表中的关联数据

6.2.3 修改

和一对多类似,只修改当前表查;

6.2.4 正向查询

查询id=2的用户所在的所有组group

u = User.objects.get(id=2)
u.groups.all()

5.3.5 反向查询

查询id=1的组中包含的所有用户

g = Group.objects.get(id=1)
g.user_set.all()

6.3 一对一关联

一对一不是数据库的一个连表操作,而是Django独有的一个连表操作。一对一关系相当于是特殊的一对多关系,只是相当于加了unique=True
一个人只能有一张身份证,一张身份证对应一个人,是一个典型的一对一关系。

class IdCard(models.Model):
    idnum = models.IntegerField()
    def __str__ (self):
        return str(self,idnum)

class Person(models.Model):
    idcard = models.OneToOneField(IdCard)
    name = models.CharField(max length=20)
    def __str__(self):
        return self.name


原文始发于微信公众号(Python之家):Django-4-模型

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/198194.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!