主要基于Django2.0.1为大家总结其中的核心技术,如果全部掌握则具备开发Web项目的能力。
为了更好的学习其知识点,我已在B站上传相关的视频:https://space.bilibili.com/485885102/channel/detail?cid=111581。如果在学习的过程中遇到问题,可以以QQ(610039018)方式沟通。
安装依赖包
pip install django==2.0.1 -i https://mirrors.aliyun.com/pypi/simple
【注意】如果Python版本(3.7.4+)很高时,SQLite3版本同样很高,则django版本建议使用django==2.1.5+;因为,admin.site 站点管理时,会报auth_user_old表不存在的错误。
进入"终端" CMD命令, 通过 django-admin 命令创建django项目。
django-admin startproject helloDjango
通过django-admin命令创建app应用
django-admin startapp mainapp
在一个Django项目中,存在很多的app应用(模块), 创建好的app需要注册到主工程中(settings.py)
Django中常用的命令包括:
项目结构如下:
helloDjango
|--- helloDjango 主工程目录
|---- settings.py # 设置文件, 数据库连接、app注册、中间件及模板配置
|---- urls.py # 总路由
|---- wsgi.py # Django实现wsgi的脚本
|---- __init__.py
|--- mainapp 应用模块(主)
|---- __init__.py
|---- admin.py # 后台管理配置脚本
|---- models.py # 数据模型类声明所在脚本
|---- views.py # 声明当前应用的视图处理函数或类
|---- urls.py # 自已增加的当前应用模块的子路由
|---- tests.py # 当前应用模块的单元测试类
|---- apps.py # 声明当前应用的基本信息
|--- manage.py WEB应用的启动脚本, 项目工程的入口
1. 到urls分发器 (总urls.py -> 子路由)
2. urls分发器根据路由规则(正则)分发到views
3. views去调用Model,交互数据
4. views将数据渲染到模板中
5. 模板呈现给用户
默认是sqlite3数据库, 在使用ORM模型之前,需要先生成迁移文件,再执行迁移命令,在数据库中生成这些模型对应的表。
python manage.py makemigrations
python manage.py migrate
注意: 一旦生成了迁移文件并且迁移成功之后,不要删除迁移文件。
在app模块中的models.py 定义一个用户(客户)模型
from django.db import models
class UserEntity(models.Model):
# 默认情况下会自动创建id主键
name = models.CharField(max_length=20)
age = models.IntegerField(default=0)
phone = models.CharField(max_lengt=11)
class Meta:
# 指定当前模型类映射成哪一个表
db_table = 'app_user'
模型创建完成后,先后执行生成迁移文件和迁移。
查询 READ
UserEntity.objects.all() # 查询所有, list
UserEngtity.objects.get(pk=id) # 根据主键值查询一个实体对象
增加 CREATE
u = UserEntity()
u.name = 'disen'
u.age = 20
u.phone = '177'
# 保存模型对象
u.save()
删除Delete
u = UserEntity.objects.get(1)
u.delete() # 删除
更新Update
u = UserEntity.objects.get(3)
u.name = '李成'
u.save()
注意:确定数据库已迁移
python manager.py createsuperuser
根据提示输入用户名、邮箱和口令
启动服务后,可以在/admin页面进行登录
在自己模块的admin.py文件中
admin.site.register(Store)
在admin.py文件中,定义admin.ModelAdmin的子类
1) 效果: 添加Store的表单中只添加name
class StoreAdmin(admin.ModelAdmin):
fields = ('name',)
admin.site.register(Store, StoreAdmin)
2) 效果: 分栏显示
class StoreAdmin(admin.ModelAdmin):
fieldsets = (['Main', {
'fields': ('name',)}],
['Advance', {
'fields': ('address',),
'classes': ('collapse',)}])
3) 效果: 内联显示
# 为外表创建内联类
class FruitInline(admin.TabularInline):
model = Fruit
# 在主表设置内联
class StoreAdmin(admin.ModelAdmin):
inlines = [FruitInline]
# 在admin.ModelAdmin的子类中
class StoreAdmin(admin.ModelAdmin):
list_display = ('id', 'name','address') # 列表显示
search_fields = ('name','address') # 搜索关键字的字段
admin.site.register(Store, StoreAdmin)
# Fruit 水果模型类(name, price, source, cate_type_id)
# FruitImage 水果图片模型类(fruit_id, url, width, height, name)
# CateType 水果分类(name, order_num)
# Store 水果商店(name,name,boss_name, phone, address, city, lat, lon)
# StoreDetail 水果商店的详情
循环语句块
{% for item in items %}
{% endfor %}
分支循环
{% if 条件表达式 %}
{% endif %}
变量
{
{ 变量名 }}
在views.py视图函数中,渲染模板
return render(request, 'fruit/list.html', locals() )
{% block name %}
{% endblock %}
{% extends "base.html" %}
字符类型,必须指定max_length属性
IntegerField
数值类型,可以指定chiose为元组,表示选择范围
BooleanField
布尔类型, 数据表中表现是0 或 1
NullBooleanField
可以为空的布尔值
AutoField
自增列 int类型。必须填入参数 primary_key=True;可选auto_create自增
FloatField
浮点类型
DecimalField
同Python的Decimal类型,参数有
TextField
文本类型
UUIDField
字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
class Order(models.Model):
no = models.UUIDField(verbose_name='订单编号')
price = models.DecimalField(verbose_name='订单金额',max_digits=6, decimal_places=2)
pay_state = models.BooleanField(default=False)
使用Order创建对象(数据库增加数据)方式如下:
import uuid
Order.objects.create(no=uuid.uuid4(),price=1819.567,pay_state=True)
# price只保留两位小数,会自动四舍五入
FileField
字符串,路径保存在数据库,文件上传到指定目录
参数有如下内容:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField
字符串,路径保存在数据库,文件上传到指定目录
参数有:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateField
日期类型
格式:YYYY-MM-DD
参数
auto_now
每次保存时,自动设置当前字段为当前时间
用于“最后一次修改时间”
auto_now_add
对象每一次保存时,自动设置为当前时间
用于“创建时间”
注意
auto_now和auto_now_add及default只能设置一个,不能组合
DateTimeField
日期时间类型
datetime.datetime
日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
TimeField
时间类型
格式: HH:MM[:ss[.uuuuuu]]
max_length
最大长度
default
默认值
unique
唯一
primary_key
主键
null
不为null
False, 不能为null
默认
True , 可以为null
blank
是否允许用户输入为空
即空字符串
db_index
索引
db_column
指定字段名
verbose_name
在admin显示的字段名称
choices
一个包含双元素元组的可迭代的对象,用于给字段提供选项
如
sex_l = (
(1,'男'),
(2,'女')
)
sex = color = models.IntegerField(choices=sex_l, defalut=1)
文件字段和图片字段都属性MEDIA的多媒体类型的文件,需要在settings.py设置脚本文件中指定MEDIA_ROOT、MEDIA_URL、STATIC_URL, STATICFILES_DIRS
使用ImageField时,必须安装Pillow库
pip install pillow
扩展: github: https://github.com/disenQF/StoreOA.git
在模型类中,声明内部类 Meta, 类成员可以如下的变量:
默认情况下,由创建模型类的元类在模型类中创建django.db.models.Manager类的对象,并赋给objects。Manager类实际是QuerySet类的子类。
我们可以在模型类中, 创建objects, 但是必须为Manager类对象。不能是普通的字段。
class CateTypeEntity(models.Model):
name = models.CharField(max_length)
# objects = models.IntegerField(default=0) # 错误的写法
objects = models.Manager() # 正确的写法
模型类.objects.filter(属性名__条件=条件值)
模型类.objects.filter(属性名=条件值)
模型类.objects.filter(属性名__时间属性__条件=条件值)
条件包含
时间属性包含
QuerySet对象的本身是可以被迭代的。
返回QuerySet对象的方法有:
统计方法和判断是否存在数据方法
django.db.models.Count/Avg/Max/Min/Sum与QuerySet的aggregate()组合使用。
django.db.models.F 用于获取字段的值并参于计算或作业更新条件。
# 中秋节: 全场水果打8.8折扣
FruitEntity.objects.update(price=F('price')*0.88)
Q是用来指定多个查询条件的逻辑关系: 且 & , 或 | , 非 ~
# 查询价格低于1的,或高于200的水果, 或源产地是西安且名字中包含"果"
fruits2 = FruitEntity.objects.filter(
Q(price__lte=1) | Q(price__gte=200) | Q(Q(source='西安') & Q(name__contains="果"))).values()
针对复杂查询来说, 通过QuerySet查询不是特别方便,则使用原生的SQL查询。
QuerySet提供两种原生SQL查询:
QuerySet.raw()
要求: 查询的字段必须是模型类中声明的字段,且必须存在主键列 。查询的结果是RawQuerySet类对象,可以迭代,元素类型是模型类对象。
另外, 查询sql语句中可以使用 “%s” 占位符, 在其它可以使用元组参数传值
raw_queryset = FruitEntity.objects.raw('select id, name,price from t_fruit where price < %s order by price DESC LIMIT %s, 10', (10, 0))
for fruit in raw_queryset:
print(fruit)
QuerySet.extra()
extra()扩展查询, 针对QuerySet查询结果集中,额外增加查询条件或排序等相关操作。返回结果还是QuerySet对象
qs1 = FruitEntity.objects.extra(where=['price<%s'], params=['10'])
qs2 = FruitEntity.objects.extra(where=['price<%s or name like %s'], params=['2', '果'])
qs3 = FruitEntity.objects.extra(where=['price<%s or name like %s', 'source=%s'], params=['2', '果', '西安'])
使用django.db.connection数据库连接对象进行原生SQL查询
connection对象表示与数据库连接的对象, 可以通过connection连接对象获取游标cursor对象,再通过cursor的execute()/fetchall()/rowcount相关方法或函数来执行原生的SQL和执行的结果。
cursor = connection.cursor()
cursor.execute('select * from t_fruit')
for row in cursor.fetchall():
print(row)
cursor.execute('update t_fruit set price=2 where id=1')
print(cursor.rowcount)
connection.commit() # 提交更新
class OrderManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(~models.Q(pay_status=5))
# 统计2018年全年销售总额
OrderModel.objects.filter(create_time__year=2018) \
.aggregate(total_price=Sum('price'))
一对一的关系可以通过models.OneToOneField(关联模型类, on_delete=models.CASCADE)建立,
on_delete除了CASCADE级联删除之外,还有model.SET_NULL级联设置为null。
class RealProfile(models.Model):
# 声明一对一的关联关系
user = models.OneToOneField(UserEntity,
verbose_name='账号',
on_delete=models.CASCADE)
real_name = models.CharField(max_length=20,
verbose_name='真实姓名')
number = models.CharField(max_length=30,
verbose_name='证件号')
real_type = models.IntegerField(verbose_name='证件类型',
choices=((0, '身份证'),
(1, '护照'),
(2, '驾驶证')))
image1 = models.ImageField(verbose_name='正面照',
upload_to='user/real')
image2 = models.ImageField(verbose_name='反面照',
upload_to='user/real')
主表: 用户表 , 从表: 实名认证表
从表对象获取 主表的数据
u1 = RealProfile.objects.filter(real_name='狄清').first()
u1.user.phone # 直接访问主表对象的数据
主表中获取从表的数据
login_u1 = UserEntity.objects.get(pk=1)
# 以隐性方式 读取从表的数据: 对象.关联模型类全小写名称.属性
login_u1.realprofile.number # realprofile 一对一的关系模型类的名称(全小写)
# 声明水果商品与购物车的关系表
class FruitCartEntity(models.Model):
cart = models.ForeignKey(CartEntity,
on_delete=models.CASCADE,
verbose_name='购物车')
fruit = models.ForeignKey(FruitEntity,
on_delete=models.CASCADE,
verbose_name='水果名')
cnt = models.IntegerField(verbose_name='数量',
default=1)
def __str__(self):
return self.fruit.name +':'+self.cart.no
@property
def price1(self):
# 属性方法在后台显示时没有verbose_name, 如何解决?
return self.fruit.price # 从获取主的对象属性
@property
def price(self):
# 属性方法在后台显示时没有verbose_name, 如何解决?
return round(self.cnt*self.fruit.price, 2)
class Meta:
db_table = 't_fruit_cart'
verbose_name_plural = verbose_name = '购物车详情表'
# 主读取多个从对象的信息
login_u = UserEntity.objects.get(pk=1)
# 查询当前用户的购物车中的所有商品及数量相关的信息
cart_fruits = login_u.cartentity.fruitcartentity_set.all()
for fruit_cart in cart_fruits:
print(fruit_cart.fruit.name, fruit_cart.fruit.price, fruit_cart.cnt)
django提供models.ManyToManyField 字段,来实现多对多的关系,需要第三方表的外键来实现。
如需求: 用户收藏商品, 需要建立第三方表,完成用户收藏多个商品, 或一个商品被多位用户收藏。
在FruitEntity模型类中,增加users属性,内容如下:
# 默认情况下,反向引用的名称是当前类的名称(小写)_set
# 可以通过related_name 来指定
# db_table='t_collect' 使用第三张表建立fruit和user的多对多关系
users = models.ManyToManyField(UserEntity,
db_table='t_collect',
related_name='fruits',
verbose_name='收藏用户列表')
当迁移完成后,模拟用户收藏商品:
u1 = UserEntity.objects.get(pk=1)
u1.fruits.add(FruitEntity.objects.get(pk=1))
u1.fruits.add(FruitEntity.objects.get(pk=2))
u2 = UserEntity.objects.get(pk=2)
u2.fruits.add(FruitEntity.objects.get(pk=2))
查询某一件商品,被哪些用户收藏
fruit1 = FruitEntity.objects.get(pk=2)
fruit1.users.all()
用户取消收藏商品
u1.fruits.remove(FruitEntity.objects.get(pk=1))
练习任务: 增加标签模型类TagEntity(id, name, order_num)并且和商品建立多对多的关系
模板加载对象: django.template.loader
template = loader.get_template('index.html')
html = template.render(context) # context是一个dict类型对象
html = loader.render_to_string("index.html", context)
使用 “.” 访问变量Variable的属性, 系统尝试以下顺序查询:
<h3>{
{ msg }}h3>
<p style="color: green">
第三个用户名: {
{ users.2.name }}
p>
<p style="color: red;">
请客的人: {
{ error_index }}
p>
<p style="color: white;background-color:blue;">
<span>VIP:span>
<span>{
{ vip.name.upper }}-{
{ vip.money }}span>
p>
<ul>
{% for key, value in vip.items %}
<li>{
{ key }} = {
{ value }}li>
{% endfor %}
ul>
<ul>
{% for user in users %}
<li>{
{ user.id }} {
{ user.name }}li>
{% if forloop.counter0 == error_index %}
<p style="color: red;">
请客的人: {
{ user.name }}
p>
{% endif %}
{% endfor %}
ul>
users = UserEntity.objects.all()
msg = '最优秀的学员'
error_index = random.randint(0, users.count()-1)
vip = {
'name': 'disen',
'money': 20000
}
# # 加载模板
# template = loader.get_template('user/list.html')
#
# # 渲染模板
# html = template.render(context={
# 'msg': msg,
# 'users': users
# })
html = loader.render_to_string('user/list.html',
locals())
return HttpResponse(html,
status=200) # 增加响应头??
or
and
not
in
数值运算
字符串
格式化
日期格式化
now|date:“Y-m-d H:i:s”
文件大小格式化
file_size|filesizeformat
小数值格式化
v|floatformat:n 默认n 是1位小数
列表
HTML转义
{
{ html|safe }} 不转义
{
{ html|escape }} 转义, 显示html源码标签
默认值
在app的__init__.py脚本编写
from django.template.defaultfilters import register
import os
@register.filter('ellipse')
def ellipse(value):
return os.path.split(value)[-1]
file_dir = os.path.join(settings.BASE_DIR, 'mainapp/')
files = {
file_name: os.stat(file_dir+file_name)
for file_name in os.listdir(file_dir)
if os.path.isfile(file_dir+file_name)}
在 模板的