16.一篇文章学会django模型的使用

1.django模型简单示例

1.1 创建django项目

创建完项目,还需要创建django子项目

django-admin startproject model_study
cd .\model_study\
python manage.py  startapp model_app

16.一篇文章学会django模型的使用

1.2配置应用

将模型对应的应用程序(刚刚创建的子应用)添加到项目的settings中:

INSTALLED_APPS = [
    'model_app',
]

在settings中配置正确的数据库连接

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

安装对应数据库的驱动

pip install mysqlclient==2.0.3

16.一篇文章学会django模型的使用

1.3 预备迁移

在项目根目录的cmd中运行

python manage.py makemigrations model_app
  • model_app是子应用的名称,如果不指定,那么就是对所有
  • INSTALLED_APPS 中的应用都进行预备迁移
    指定该命令后,在对应的子应用下的 migrations 中会生成一个对应的迁移文件

1.4编写模型

在子应用的models.py中

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField()

1.5 正式迁移

python manage.py migrate model_app

1.6 效果展示

16.一篇文章学会django模型的使用

; 2.django模型修改后重新应用

2.1概述

不管是新增模型,还是修改已有模型后,在编写完模型后,只需要重复1.3预备迁移和正式迁移步骤,会在migrations中自动生成新的迁移文件。即自动实现数据库中的表结构、表关系等信息的修改

2.2 在1.4基础上继续修改已有模型代码

from django.db import models

class Question(models.Model):
    question_title = models.CharField(max_length=20)
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField()

2.3预备迁移与正式迁移

在项目根目录的cmd中运行

python manage.py makemigrations model_app

此时出现报错
You are trying to add a non-nullable field ‘question_title’ to question without a default; we can’t do that (the database
needs something to populate existing rows).

Please select a fix:

  1. Provide a one-off default now (will be set on all existing rows with a null value for this column)
  2. Quit, and let me add a default in models.py
    Select an option:
    大概意思就是,本身已有的字段,需要为新的字段设置一个默认值,此时我们给新字段多加一个空值。
    修改模型代码如下
from django.db import models

class Question(models.Model):
    question_title = models.CharField(max_length=20, default='')
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField()

再次执行预备迁移命令
执行正式迁移命令

python manage.py migrate model_app

2.4效果展示

16.一篇文章学会django模型的使用

; 3.Django逆向从数据库表生成模型类

3.1创建新的Django应用

创建新的Django项目(reverse_model_study)与子应用(model_app)方法参考1.1,配置方法参考1.2

3.2 在navicat中新建表

创建新的数据库,然后新建表

16.一篇文章学会django模型的使用

; 3.2生成模型类

在根目录的cmd中运行:

python manage.py inspectdb > model_app/models.py

偶遇报错django.db.utils.OperationalError: unable to open database file
解决方法:发现是在1.2中的配置mysql出现错误,splite没改成mysql

3.3自动生成表结构

16.一篇文章学会django模型的使用

; 4.Django项目使用sqlite3数据库

4.1 准备项目

按照1.1方法创建django项目,并配置应用(不配置数据库的)

4.2 创建模型类

在子应用的models中

from django.db import models

class Student(models.Model):
    sname = models.CharField(max_length=20)
    score = models.CharField(max_length=20)
    age = models.CharField(max_length=20)

4.3 预备迁移与正式迁移

参考1.3与1.5即可
偶遇报错:model_app.Student.score: (fields.E120) CharFields must define a ‘max_length’ attribute.

原因是我的模型中对CharFild的score并没有给最大长度限制(上面的已修改)

4.4 效果展示

16.一篇文章学会django模型的使用

; 5.字段Field

5.1 字段命名限制

  • 字母,数字,下划线,首字母不能是数字
  • 字段名称不能是Python保留字
  • 由于Django查询查找语法的工作方式,字段名称不能在一行中包含多个下划线,譬如”abc__123″就是不允许的,一个下划线是可以的’abc_123′

5.2 AutoField、ID、主键PRIMARY_KEY

​ 默认会自动创建一个自增,主键的id列

​ 如果指定了 primary_key 为其它列,那么不会自动创建id列

​ 可以在模型中指定(自动生成主键):

id = models.AutoField(primary_key=True)

5.3常见的Filed

所有的Field类型,见 官方网址

16.一篇文章学会django模型的使用
特殊使用:uuid不加括号
import uuid
from django.db import models

class MyUUIDModel(models.Model):

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

5.4 Field常见参数

  • max_length:字段最大长度,用于字符串等,字符串类型CharField必须设置该值
  • null:如果True,Django将在数据库中存储NULL空值。默认是False
  • blank:如果True,该字段被允许为空白(“”)。默认是False。请注意,这不同于null。null纯粹是与数据库相关的,而blank与验证相关。如果一个字段有blank=True,表单验证将允许输入一个空值。如果一个字段有blank=False,该字段将是必需的。
  • choices:示例:YEAR_IN_SCHOOL_CHOICES = ((‘FR’, ‘Freshman’),(‘SO’, ‘Sophomore’),(‘JR’, ‘Junior’),(‘SR’, ‘Senior’),(‘GR’, ‘Graduate’)) ,中文示例:SEX_CHOICES=((1, ‘男’),(2, ‘女’)),元组中的第一个元素是将存储在数据库中的值,第二个元素是将在页面中显示的值,最常见用于下拉选择框select
  • default:字段的默认值
  • help_text:用于显示额外的”帮助”文本
  • primary_key:如果True,这个字段是模型的主键,默认是False
  • unique:如果True,该字段在整个表格中必须是唯一的
  • verbose_name:详细字段名,不指定则是属性名的小写,并且用 空格 替换 ‘_’

6.模型之间的关系

6.1 模型一对多关系映射

概述

  • 主外关系中,关联操作最常用的是: models.CASCADE 级联删除(主表子表一起删) 和 models.SET_NULL 设置为null
  • 一对多关系中,ForeignKey 写在一对多关系中,多的那个模型中
    例如:使用django.db.models.ForeignKey,例如,如果一个Car模型有一个Manufacturer,也就是说,一个Manufacturer可以对应多个汽车,但Car只有一个汽车生产商Manufacturer。Car的数量是多种,所以他是外键。
from django.db import models

class Manufacturer(models.Model):
    name = models.CharField(max_length=20)

class Car(models.Model):

    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    name = models.CharField(max_length=20)

效果展示

16.一篇文章学会django模型的使用

6.2 模型一对一关系映射

使用django.dbOneToOneField,例如,地址Place和餐馆Restaurant是一对一关系,而餐馆Restaurant和服务员Waiter是一对多关系

![from django.db import models
from django.db.models import CharField,CASCADE,Model

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=100)

    def __str__(self) -> str:
        return "%s the place" % self.name

class Restaurant(Model):

    place = models.OneToOneField(Place, on_delete=CASCADE, primary_key=True)

    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

    def __str__(self) -> str:
        return "%s the restaurant" % self.place.name

class Waiter(Model):
    restaurant = models.ForeignKey(Restaurant, on_delete=CASCADE)
    name = CharField(max_length=150)

    def __str__(self) -> str:
        return "%s the waiter at %s" % (self.name, self.restaurant)

16.一篇文章学会django模型的使用

6.3 模型多对多关系映射

自关联
对于多对多表,例如学生表,一个学生可能会有多位好友。然而好友和学生都是同属于学生表的,这个就是最简易的多对多表——其实他们都是通过生成一张中间表(会自动生成),拆分成一对多的关系进行存储

from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=20)
    friends = models.ManyToManyField("self")

简易多对多
对于教室和老师关系,每个老师需要在多个教室上课,每个教室会有多个老师来上课。

from django.db import models
class SchoolClass(models.Model):
    name = models.CharField(max_length=20)

class Teacher(models.Model):
    name = models.CharField(max_length=20)
    school_class = models.ManyToManyField(SchoolClass)

16.一篇文章学会django模型的使用
自定义中间表
不使用Django中自动生成的中间表:
人与组之间的关系也是一种多对多关系
from django.db import models
class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

class Group(models.Model):
    name = models.CharField(max_length=128)

    members = models.ManyToManyField(Person,through='Membership', through_fields=('group', 'person') )

class Membership(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    level = models.IntegerField(default=1)

16.一篇文章学会django模型的使用
  • 通过 through=’Membership’ 指定Membership作为中间表
  • 通过 through_fields=(‘group’, ‘person’) 指定中间模型的属性
  • 一般需要自定义中间表时,都是有额外的字段,譬如 level = models.IntegerField(default=1)

7.Django模型之方法

除了运行程序时,可以测试模型,还可以在根目录的cmd执行:
先进入父级目录,执行以下代码

python manage.py shell

​打开django脚本控制台,测试执行模型的方法,会比启动项目更方便

模型对象中有一个objects属性,该属性是管理器Manager类型的对象,几乎所有的方法都是通过该对象执行的,具体见下面的代码:

7.1 新增数据

具体的字段可以从第6点中查看,在VSCode的终端运行
普通模型插入
save或create

from model_app.models import *

Question.objects.create(question_title='insert1',question_text='insert_content',pub_date='2020-1-1')

p1=Question(question_title='insert1',question_text='insert_content',pub_date='2020-1-1')
p1.save()

一对一关系插入
首先,先增加未使用OneToOneField这一方,查询出来后作为一个对象,作为字段插入另一个使用OneToOneField这一方

from one_one_app.models import *

Place.objects.create(name='北京',address='北京饭店')

place = Place.objects.get(pk=1)

Restaurant.objects.create(place=place)

一对多关系插入
方法1:还是按照一对一方式
方法2:先添加其中一方,然后再创建多方对象,设置外键(可以选择直接给主表的对象,也可以赋值关联的外键),保存对象
这里就演示方法2


r = Restaurant.objects.get(pk=1)
Waiter.objects.create(restaurant=r,name="小张")

r = Restaurant.objects.get(pk=1)
W = Waiter(name="小李")
W.restaurant = r
W.save()

w = Waiter(name="小王")
w.restaurant_id = 1
w.save()

多对多关系插入
先对两张主表进行插入数据,用普通模型插入方式,再获取其中一方的对象,使用set方法为中间表赋值。
给中间表赋值有两种方法,一种是set,一种是add。set在二次插入时,会重新赋值,add是增加,遇到重复则保留。


Teacher.objects.create(name="李老师")
Teacher.objects.create(name="王老师")
Teacher.objects.create(name="张老师")
SchoolClass.objects.create(name="班级1")
SchoolClass.objects.create(name="班级2")
SchoolClass.objects.create(name="班级3")

t = Teacher.objects.get(pk=1)
t.school_class.set([1,2,3])
t.school_class.set([1,2])

t = Teacher.objects.get(pk=2)
t.school_class.add(1,2,3)
t.school_class.add(1,2)

7.2 查询数据

7.2.1 常规查询与过滤器

  • 大部分检索是懒惰执行的,只在真实获取值的时候,才会去连接数据库获取数据
  • 查询集通过过滤器进行查询,允许链式调用
  • pk是主键的别名(primary key),如果真实主键是id,那么 pk 和 id 使用是一样的效果

>>> from model_app.models import Question
>>> Question.objects.get(id=1)
(1)>
>>> Question.objects.get(pk=2)
(2)>

>>> Question.objects.all()
[(1)>, (2)>]>

>>> Question.objects.filter(question_title='insert1')
[(1)>]>

7.2.2 字段查找

字段查找

  • 字段检索,是在字段名后加 ‘__’ 双下划线,再加关键字,类似 SQL 语句中的 where 后面的部分, 如: 字段名__关键字
  • 在查找中指定的字段必须是模型字段的名称,但有一个例外,如果是ForeignKey字段,则是属性名+ ‘_id’: Entry.objects.filter(blog_id=4) , 定义的 ForeignKey是 blog
  • 完整的字段检索文档: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#field-lookups 常见的字段检索: exact :判断是否等于value,一般不使用,而直接使用 ‘=’ contains:是否包含,大小写敏感,如果需要不敏感的话,使用icontains startswith:以value开头,大小写敏感 endwith:以value结尾,大小写敏感 in:是否包含在范围内 isnull:是否为null, 如:filter(name__isnull=Flase) gt:大于,如:filter(sage__gt=30) , 年龄大于30 gte:大于等于 lt:小于 lte:小于等于
>>> Question.objects.filter(id__exact=1)
[(1)>]>
>>> Question.objects.filter(id=1)
[(1)>]>

>>> Question.objects.filter(question_text__contains='i')
[(1)>, (2)>]>
>>> Question.objects.filter(question_text__contains='I')
[]>
>>> Question.objects.filter(question_text__icontains='I')
[(1)>, (2)>]>

>>> Question.objects.filter(question_text__startswith='I')
[]>
>>> Question.objects.filter(question_text__istartswith='I')
[(1)>, (2)>]>
>>> Question.objects.filter(question_text__iendswith='T')
[(1)>, (2)>]>

>>> Question.objects.filter(id__in=(1,2))
[(1)>, (2)>]>

>>> Question.objects.filter(question_text__isnull=True)
[]>
>>> Question.objects.filter(question_text__isnull=False)
[(1)>, (2)>]>

>>> Question.objects.filter(id__gt=1)
[(2)>]>
>>> Question.objects.filter(id__gte=1)
[(1)>, (2)>]>
>>> Question.objects.filter(id__lt=2)
[(1)>]>
>>> Question.objects.filter(id__lte=2)
[(1)>, (2)>]>

7.2.3 日期时间的过滤

year/month/day/week_day/hour/minute/second:时间查询,如: filter(pub_date__year=2015) 年份是2015的, filter(pub_date__day=15) 天数是15的


Account.objects.filter(register_date__gt='2021-7-1')

Account.objects.filter(register_date__range=('2021-7-1','2021-7-7'))

Account.objects.filter(register_date__date='2021-7-6')
[(2)>, (3)>]>

persons = Person.objects.exclude(pub_date__year=2006)

persons = Person.objects.filter(pub_date__year=2006)

persons = Person.objects.filter(pub_date__year=2006)[:5]

persons = Person.objects.filter(pub_date__year=2006)[5:10]

Entry.objects.all()[:10:2]

Account.objects.filter(register_date__month=7)

Account.objects.filter(register_date__year=2021,register_date__day=6)
Account.objects.filter(register_date__year=2021).filter(register_date__day=6)

Account.objects.filter(register_date__week_day=3)

7.2.4 order_by排序


>>> Question.objects.order_by('pub_date')
[(4)>, (1)>, (6)>, (3)>, (5)>, (2)>]>

>>> Question.objects.order_by('-pub_date')
[(2)>, (5)>, (3)>, (6)>, (1)>, (4)>]>

>>> Question.objects.order_by('-pub_date','id')
[(2)>, (5)>, (3)>, (6)>, (1)>, (4)>]>

7.2.5 查询的其他方法


count = Person.objects.filter(pub_date__year=2006).count()

Account.objects.all().order_by('id').reverse()[:1]

person = Person.objects.filter(pub_date__year=2006).first()
person = Person.objects.first()

person = Person.objects.filter(pub_date__year=2006).last()

person_dict_li = Person.objects.filter(pub_date__year=2006).values()

Account.objects.all().values('user_name')
[{'user_name': 'lili'}, {'user_name': 'tom'}, {'user_name': '李四'}]>

Account.objects.all().values('user_name','register_date')
[{'user_name': '李四', 'register_date': datetime.datetime(2021, 7, 1, 3, 18, 18, tzinfo=)}, {'user_name': 'tom', 'register_date': datetime.datetime(2021, 7, 6, 1, 52, 54, 5896, tzinfo=)}, {'user_name': 'lili', 'register_date': datetime.datetime(2021, 7, 6, 2, 10, 42, 927481, tzinfo=)}]>

只有真正取值时候,才会连接数据库获取数据—— 懒惰查询

7.2.6 多对象关联查询

当我们需要在一对一关系、一对多关系、多对多关系中获取数据时,这就需要使用我们的多对象关联查询

  1. 一对一关系,通过一个模型获取另一个模型
    得先判定当前表是否有关联关系,是两种方法:

>>> Restaurant.objects.filter(place__address__startswith='北京')
[]>
>>> Restaurant.objects.filter(place__name__startswith='北京')
[]>

>>> Restaurant.objects.first().place

>>> Place.objects.get(pk=1).restaurant

  1. 一对多关系,通过一个模型获取另一个模型
    得先判定当前表是否有关联关系,是两种方法:

>>> r = Restaurant.objects.get(pk=1)
>>> r.waiter_set.all()
[, , ]>
  1. 多对多关系,通过一个模型获取另一个模型
    得先判定当前表是否有关联关系,是两种方法:

>>> t = Teacher.objects.first()
>>> t
(1)>
>>> t.school_class.all()
[(1)>, (2)>]>

>>> s = SchoolClass.objects.first()
>>> s
(1)>
>>> s.teacher_set.all()
[(1)>, (2)>]>

7.2.7 聚合函数

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

常用的聚合函数有:Avg、Count、Max、Min、Sum

>>> from model_app.models import *
>>> from django.db.models import *

>>> Question.objects.aggregate(Max('id'))
{'id__max': 6}

>>> Question.objects.aggregate(idMax=Max('id'))
{'idMax': 6}

>>> Question.objects.aggregate(idMax=Max('id'), idMin=Min('id'), idAvg=Avg('id'))
{'idMax': 6, 'idMin': 1, 'idAvg': 3.5}

7.2.8 分组查询

使用annotate()函数实现分组查询,得配合其他函数:

  • annotate:用于分组,配合 Avg,Count等聚合函数,如:annotate(max=Max(‘age’))
  • filter: 用于过滤,在 annotate之前使用表示 where 条件,在annotate之后使用表示having条件
  • values:在annotate之前使用表示分组字段,在annotate之后使用表示取值

Teacher.objects.values('school_class').annotate(tcount=Count('id'))
[{'school_class': 1, 'tcount': 2}, {'school_class': 2, 'tcount': 2}, {'school_class': 3, 'tcount': 1}, {'school_class': None, 'tcount': 1}]>

>>> SchoolClass.objects.values('teacher').annotate(tcount=Count('id'))
[{'teacher': 1, 'tcount': 2}, {'teacher': 2, 'tcount': 3}]>

>>> SchoolClass.objects.values('teacher').annotate(tcount=Count('id')).filter(name__contains='班级2')
[{'teacher': 1, 'tcount': 1}, {'teacher': 2, 'tcount': 1}]>
>>> SchoolClass.objects.values('teacher').filter(name__contains='班级2').annotate(tcount=Count('id'))
[{'teacher': 1, 'tcount': 1}, {'teacher': 2, 'tcount': 1}]>

7.3 修改数据

单条语句修改,获取对象后,直接对对象的属性名进行修改,再save
多条语句修改,先获取多条语句对象,再使用update就行

>>> q = Question.objects.first()
>>> q.question_title = 'update title'
>>> q.save()
>>> Question.objects.filter(pub_date__day='1').update(question_title='update titles')
2

7.4 删除数据

删除数据直接获取对象,然后执行delete方法即可

>>> Question.objects.filter(pub_date__day='1').delete()
(2, {'model_app.Question': 2})

7.5 刷新对象

当我们从数据库先获取了一个对象,但是如果这之后进行了修改,需要刷新对象,重新获取。就避免了再一次的编写获取对象的代码

>>> q = Question.objects.first()
>>> q.question_text
'insert2_content'

>>> q.question_text
'insert2_content'
>>> q.refresh_from_db()
>>> q.question_text
'insert2_content7777'

7.6 模型Q对象的使用

Q对象就是完成过滤时的且、或、非等操作
filter() 等方法中的关键字参数查询都是并且(‘AND’)的, 如果你需要执行更复杂的查询(例如or语句),那么可以使用Q 对象。

​ Q 对象 (django.db.models.Q) 对象用于封装一组关键字参数,可以使用 & 和 | 操作符组合起来,当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。

>>> from django.db.models import Q

>>> Question.objects.filter(Q(pub_date__year='2021') | Q(question_text__startswith='O'))
[(3)>, (4)>]>

>>> Question.objects.filter(Q(pub_date__year='2021') & Q(question_text__startswith='O') | Q(question_text__startswith='N'))
[(3)>]>

>>> Question.objects.filter(Q(pub_date__year='2021'))
[(3)>]>
>>> Question.objects.filter(~Q(pub_date__year='2021'))

[(2)>, (4)>, (6)>]>
>>> Question.objects.filter(Q(pub_date__year='2021'), pub_date__year='2021')
[(3)>]>

>>> Question.objects.filter(pub_date__year='2021',Q(pub_date__year='2021'))
  File "", line 1
    Question.objects.filter(pub_date__year='2021',Q(pub_date__year='2021'))
                                                                          ^
SyntaxError: positional argument follows keyword argument

7.7 模型F对象的使用

模型的属性名出现在操作符的右边,就使用F对象进行包裹


grades = Grade.objects.filter(girlnum__gt=F('boynum'))

Grade.objects.filter(girlnum__gt=F('boynum') + 10)

Book.objects.update(price=F("price")+1)

8.sql语句的使用

8.1通过模型使用sql

通过raw函数执行原始的sql语句进行查询


class Person(models.Model):
    first_name = models.CharField()
    last_name = models.CharField()
    birth_date = models.DateField()

for p in Person.objects.raw('SELECT * FROM myapp_person'):
    print(p)

Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person')

Person.objects.raw('SELECT last_name, birth_date, first_name, id FROM myapp_person')

Person.objects.raw('SELECT first AS first_name, last AS last_name,bd AS birth_date,pk AS id,FROM some_other_table')

first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]

first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]

lname = 'Doe'
Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])

不应用模型,直接执行sql

from django.db import connection

def my_custom_sql(obj):
    with connection.cursor() as cursor:
        cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [obj.baz])
        cursor.execute("SELECT foo FROM bar WHERE baz = %s", [obj.baz])
        row = cursor.fetchone()

    return row

Original: https://blog.csdn.net/m0_63953077/article/details/127908912
Author: 想成为数据分析师的开发工程师
Title: 16.一篇文章学会django模型的使用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/732670/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球