django的命令
下载安装
pip install django==1.11.28 -i 源
django-admin startproject 项目名称
切换到项目的根目录
python manage.py runserver
127.0.0.1:8000
python manage.py runserver 80
127.0.0.1:80
python manage.py runserver 0.0.0.0:80
0.0.0.0:80
python manage.py startapp app名称
python manage.py makemigrations # 制作迁移文件 检测所有注册的APP下的models的变更,记录下变更记录
python manage.py migrate # 迁移 将变更记录同步到数据库中
2.django的配置settings.py
BASE_DIR 项目的根目录
INSTALLED_APPS = [
‘app01.apps.App01Config’
]
MIDDLEWARE
注释掉一个csrf的中间件,可以提交POST请求
TEMPLATES
DIRS [ os.path.join(BASE_DIR,‘tempaltes’) ]
DATABASES 数据库
静态文件
STATIC_URL = ‘/static/’ 访问静态文件的路径
STATICFILES_DIRS = [ 包含静态资源的文件
os.path.join(BASE_DIR,‘static’)
]
3.django项目使用mysql数据库的流程
手动创建一个mysql数据库
在settings.py中配置数据库的连接
ENGINE 引擎 mysql
NAME 数据库的名字
HOST 数据库的所在的IP
PORT 3306
USER 用户名
PASSWORD 密码
写在与项目同名的目录下的__init__.py
中
import pymysql
pymysql.install_as_MySQLdb()
class User(models.Model):
username = models.CharField(max_length=32) # varchar(32)
python manage.py makemigrations
python manage.py migrate
4.request
request.method 请求方法 GET POST
request.GET URL上携带的参数 ?k1=v1 {}
request.POST post请求提交的数据 {}
5.response
HttpResponse(‘字符串’) 返回的是字符串
render(request,‘模板的名字’,{‘k1’:v1}) 返回一个完整的HTML页面
redirect(‘地址’) 重定向
6.ORM
对象关系映射
对应关系
类 ——》 表
对象 ——》 数据行(记录)
属性 ——》 字段
class Publisher(models.Model):
name = models.CharField(max_length=32)
class Book(models.Model):
name = models.CharField(max_length=32)
pub = models.ForeginKey('Publisher',on_delete=models.CASCADE)
class Author(models.Model):
name = models.CharField(max_length=32)
books = models.ManyToManyField('Book') # 不创建字段,创建第三张表
ORM操作:
1.查询
from app01 import models
models.Publisher.objects.all() # 查询所有的数据 QuerySet 对象列表
models.Publisher.objects.get(name='xx',pk='1') # 获取有且唯一的一个的对象
models.Publisher.objects.filter(name='xx',pk='1') # 获取多个对象 对象列表
pub_obj.pk pub_obj.name
book_obj.pub # 外键 所关联的对象Publisher对象
book_obj.pub_id # 所关联对象的id
author_obj.books # 多对多字段 关系管理对象
author_obj.books.all() # 关联的所有的对象 对象列表
2.新增
models.Publisher.objects.create(name='xxx')
models.Book.objects.create(name='xxx',pub=出版社对象)
models.Book.objects.create(name='xxx',pub_id=出版社对象的id)
author_obj = models.Author.objects.create(name='xxx')
author_obj.books.set([1,2]) # 设置多对多关系
3.删除
models.Publisher.objects.get(pk=1).delete() # 通过对象进行删除
models.Publisher.objects.filter(pk=1).delete() # 批量删除
4.修改
pub_obj.name = 'xxxx'
pub_obj.save() # 提交到数据库中保存
models.Publisher.objects.filter(pk=1).update(name='xxxx')
7.模板
render(request,‘模板的名字’,{‘k1’:v1})
{{ k1 }}
{% for i in list %}
{{ forloop.counter }}
{{ i }}
{% endfor %}
{% if 条件 %}
x1
{% elif 条件1 %}
x2
{% else %}
else
{% endif %}
MVC:
M:model 模型 操作数据库
V:view 视图 展示数据 HTML
C:controller 控制器 流程 业务逻辑
MTV:
M:model ORM
T: template 模板
V:view 视图 业务逻辑
模板语法
变量 {{ 变量 }}
.
.索引 .key .属性 .方法 方法后不加括号
优先级:
.key > .属性 .方法 > .索引
过滤器:
{{ 变量|过滤器:‘参数’ }}
default:
变量不存在或者为空时使用默认值
add +
数字的加法,字符串和列表的拼接
date
{{ now|date:'Y-m-d H:i:s' }}
settings
USE_L10N = False
DATETIME_FORMAT = 'Y-m-d H:i:s'
TIME_FORMAT = 'H:i:s'
DATE_FORMAT = 'Y-m-d'
在一个已经注册的app下创建一个名为templatetags的python包 (包的名字不能错)
创建一个python文件,文件名自定义(mytags.py)
在python文件中写:
from django import template
register = template.Library() # register的名字不能错
写函数 + 加装饰器
@register.filter
def add_arg(value, arg):
# 功能
return "{}_{}".format(value, arg)
@register.filter
def add_arg(value,arg):
return 'xxxx'
@register.simple_tag
def str_join(*args, **kwargs):
return "{}_{}".format('_'.join(args), '*'.join(kwargs.values()))
@register.inclusion_tag('page.html')
def pagination(num):
return {'num': range(1, num + 1)}
page.html
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«span>
a>
li>
{% for li in num %}
<li><a href="#">{{ li }}a>li>
{% endfor %}
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»span>
a>
li>
ul>
nav>
5.使用
{% load mytags %}
{{ 'alex'|add_arg:'dsb' }}
{% str_join 'a' 'b' 'c' k1='d' k2='e' k3='f' %}
{% pagination 10 %}
mark_safe
from django.utils.safestring import mark_safe
@register.filter
def show_a(name, url):
return mark_safe('{}'.format(url, name))
for
{% for name in name_list %}
- {{ forloop.counter }}-{{ name }}
{% endfor %}
forloop.counter 当前循环的序号 从1开始
forloop.counter0 当前循环的序号 从0开始
forloop.revcounter 当前循环的序号(倒序) 到1结束
forloop.revcounter0 当前循环的序号(倒序) 到0结束
forloop.first 是否是第一次循环 布尔值
forloop.last 是否是最后一次循环 布尔值
forloop.parentloop 当前循环的外层循环的变量
{% for foo in kong %}
{{ foo }}
{% empty %}
空空如也
{% endfor %}
if
{% if alex.age < 73 %}
alex正迈向第一个坎
{% elif alex.age == 73 %}
alex正在第一个坎上
{% elif alex.age < 84 %}
alex正迈向第二个坎上
{% elif alex.age == 84 %}
alex正在第二个坎上
{% elif alex.age > 84 %}
差不多了
{% endif %}
注意:
{% if 10 > 5 > 1 %}
正确1
{% else %}
错误
{% endif %}
with
{% with p_list.0.name as alex %}
{{ alex }}
{{ alex }}
{{ alex }}
{% endwith %}
{% with alex=p_list.0.name %}
{{ alex }}
{{ alex }}
{{ alex }}
{% endwith %}
csrf_token
{% csrf_token %}写在form标签中,form标签中就有一个隐藏的input标签,name=‘csrfmiddlewaretoken’
django处理浏览器的请求的流程:
发请求的途径
get和post请求的区别
get 获取一个资源
?k1=v1&k2=v2 request.GET
get请求没有请求体
post 提交数据
request.POST
数据在请求体中
views.py
def login(request):
# 业务逻辑
return 响应
HttpResponse('字符串') # 返回一个字符串
render(request,'模板的文件名',{‘k1’:v1})
redirect('地址') # 重定向
request.method # 请求方式 GET POST
ORM
models.py
class Publisher(models.Model):
name = models.CharField(max_length=32) # varchar(32)
class Book(models.Model):
name = models.CharField(max_length=32) # varchar(32)
pub = models.ForeignKey('Publisher',on_delete=models.CASCADE)
"""
on_delete 2.0版本后是必填的
models.CASCADE 级联删除
models.PROTECT 保护
models.SET(v) 删除后设置为某个值
models.SETDEFAULT 删除后设置为默认值
models.SET_NULL 删除后设置为Null
models.DO_NOTHING 什么都不做
"""
orm的操作:
查
from app01 import models
models.Publisher.objects.all() # 获取所有的数据 QuerySet 对象列表
models.Publisher.objects.get(name='xxx',id='1') # 获取一条存在且唯一的数据 对象
models.Publisher.objects.filter(name='xxx',id='1') # 获取多条的数据 对象列表
ret = models.Book.objects.all() # 对象列表
for book in ret:
print(book)
print(book.id,book.pk)
print(book.name)
print(book.pub) # 书籍所关联的出版社的对象
print(book.pub_id) # 书籍所关联的出版社的id
新增:
models.Publisher.objects.create(name='xxx') # 新增的对象
models.Book.objects.create(name='xxx',pub=出版社的对象) # 新增的对象
models.Book.objects.create(name='xxx',pub_id=出版社的id) # 新增的对象
删除
models.Publisher.objects.get(pk=1).delete()
models.Publisher.objects.filter(pk=1).delete() # 批量删除
编辑
book_obj.name = 'xxx'
# book_obj.pub= 出版社对象
# book_obj.pub_id= 出版社的id
book_obj.save() # 保存到数据库
models.Book.objects.filter(pk=1).update(name='xx',pub_id=出版社的id) # 批量更新
模板的语法
return render(request,‘模板的文件名’,{‘k1’:v1,‘k2’:v2})
{{ k1 }} {{ k2 }}
for
{% for i in k1 %}
{{ forloop.counter }}
{{ i }}
{% endfor %}
if
{% if 条件 %}
xxx
{% endif %}
{% if 条件 %}
xxx
{% else %}
x1
{% endif %}
{% if 条件 %}
xxx
{% elif 条件1 %}
xx
{% else %}
x1
{% endif %}
作者表增删改查
# 作者表
class Author(models.Model):
name = models.CharField(max_length=32)
books = models.ManyToManyField('Book') # 不在Author表中新增字段,会创建第三章表
all_authors = models.Author.objects.all()
for author in all_authors:
print(author)
print(author.id)
print(author.name)
print(author.books) # 关系管理对象
print(author.books.all()) # 所关联的所有的对象
print('*' * 40)
展示
<table border="1">
<thead>
<tr>
<th>序号th>
<th>IDth>
<th>姓名th>
<th>代表作th>
tr>
thead>
<tbody>
{% for author in all_authors %}
<tr>
<td>{{ forloop.counter }}td>
<td>{{ author.pk }}td>
<td>{{ author.name }}td>
<td>
{% for book in author.books.all %}
{{ book.name }}
{% endfor %}
td>
tr>
{% endfor %}
tbody>
table>
新增
author_name = request.POST.get('author_name')
book_ids = request.POST.getlist('book_ids') # 获取多个数据 list类型
# print(book_ids,type(book_ids))
# 向作者表插入了作者的信息
author_obj = models.Author.objects.create(name=author_name)
# 该作者和提交的书籍绑定多对多的关系
author_obj.books.set(book_ids) # 设置多对多关系
编辑
author_name = request.POST.get('author_name')
book_ids = request.POST.getlist('book_ids')
# 给该对象修改数据
# 修改作者的姓名
author_obj.name = author_name
author_obj.save()
# 修改作者和书的多对多关系
author_obj.books.set(book_ids) # 重新设置
{{ }} 变量
{% %} 标签
.
{{ 变量.索引 }}
{{ 变量.key }}
{{ 变量.属性 }}
{{ 变量.方法 }}
优先级:
.key > .属性和方法 > .索引
过滤器
{{ 变量|filter }} {{ 变量|filter:参数 }}
内置的过滤器
default 给变量加默认值
add + 数字的加法 字符串的拼接 列表的拼接
safe 告诉django前面的字符串不需要做转义
upper lower title
filesizeformat 文件的大小显示成带单位的
slice 切片 {{ list|slice:‘::’ }}
length 返回变量的长度
truncatechars 根据字符的长度进行截断
truncatewords 根据单词的长度进行截断
join 字符串的拼接
date 日期时间的格式化 ‘Y-m-d H:i:s’
TIME_ZONE = ‘Asia/Shanghai’
USE_L10N = False
DATETIME_FORMAT = ‘Y-m-d H:i:s’
DATE_FORMAT = ‘Y-m-d’
TIME_FORMAT = ‘H:i:s’
母版:
继承:
1. {% extends '母版的名字' %}
2. 重新复写block块
注意点:
{% extends ‘母版的名字’ %} 母版的名字带引号
{% extends ‘母版的名字’ %}写在第一行,上面不再写内容
要显示的内容要写在block块中
多写一个css/js的block块
组件
把一小段公用的HTML文本写入一个HTML文件 , nav.html
在需要该组件的模板中
{% include 'nav.html' %}
静态文件相关:
{% load static %}
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/dsb.css' %}">
静态文件
配置
STATIC_URL = ‘/static/’ 访问静态文件的路径
STATICFILES_DIRS = [
os.path.join(BASE_DIR,‘static’) 包含静态文件的文件名
]
引入
<link rel="stylesheet" href="/static/相对路径">
<script src="/static/相对路径">script>
{% load static %}
<link rel="stylesheet" href="{% static '相对路径' %}">
FBV function based view
def xxx(request):
# 业务逻辑的处理
return response
# urls.py
url(r'xxx/',xxx)
CBV class based view
from django.views import View
class Xxx(View):
def get(self,request)
# 专门处理get请求
return response
def post(self,request)
# 专门处理post请求
return response
url(r'xx/',Xxx.as_view())
as_view()的流程
项目运行时加载urls.py的文件,执行类.as_view()方法
as_view()执行后,内部定义了一个view函数,并且返回。
请求到来的时候,执行view函数:
实例化类 —— 》 self
self.request = request
执行self.dispatch(request, *args, **kwargs)的方法
判断请求方式是否被允许
允许:
通过反射获取请求方式对应的请求方法 ——》 handler
获取不到 self.http_method_not_allowed ——》 handler
不允许:
self.http_method_not_allowed ——》 handler
执行handler,返回结果
from functools import wraps
def timer(func):
@wraps(func)
def inner(request, *args, **kwargs):
start = time.time()
ret = func(request, *args, **kwargs)
print('执行的时间是:{}'.format(time.time() - start))
return ret
return inner
FBV
直接加在函数上就行
CBV加装饰器:
需要使用一个装饰器
from django.utils.decorators import method_decorator
加在方法上
@method_decorator(timer)
def get(self, request):
加在dispatch方法上
@method_decorator(timer)
def dispatch(self, request, *args, **kwargs):
# 之前的操作
ret = super().dispatch(request, *args, **kwargs) # 执行View中的dispatch方法
# 之后的操作
return ret3
@method_decorator(timer,name='dispatch')
class PublisherAdd(View):
加在类上
@method_decorator(timer,name='post')
@method_decorator(timer,name='get')
class PublisherAdd(View):
request.method 请求方法 GET POST
request.GET URL上携带的参数 ?k1=v1&k2=v2 { }
request.POST post请求提交的数据 {} 编码方式是URLencode
request.path_info 路径信息 不包含IP和端口 也不包含参数
request.body 请求体 bytes类型
request.COOKIES cookie
request.session session
request.FILES 长传的文件
request.META 头的信息 小写——》大写 HTTP_ 开头 - ——》 _
request.get_full_path() 完整的路径信息 不包含IP和端口 包含参数
request.is_ajax() 请求是否是ajax请求
from django.shortcuts import render, HttpResponse, redirect
HttpResponse('字符串') # 返回字符串
render(request,'模板的文件名',{'k1':v1}) # 返回一个HTML页面
redirect('地址') # 重定向 Location:‘地址’ 301 302
from django.http.response import JsonResponse
JsonResponse({'k1':'v1'})
JsonResponse(data,safe=False)
urls.py
url(r'^upload/', views.Upload.as_view()),
视图:
class Upload(View):
def get(self, request):
return render(request, 'upload.html')
def post(self, request):
file = request.FILES.get('f1')
with open(file.name, 'wb') as f:
for i in file:
f.write(i)
return HttpResponse('ok')
upload.html
f1 = request.FILES.get('f1')
f1.name # 上传文件的名字
for i in f1:
f.write(i)
from django.conf.urls import url
urlpatterns = [
url(正则表达式, views视图,参数,别名),]
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^blog/$', views.blog),
url(r'^blog/[0-9]{4}/\d{2}/$', views.blogs),
]
^ 以什么开头
$ 以什么结尾
\d 数字
\w 数字字母下划线
{}
[a-z0-9]
. 匹配换行符之外的标志
+ 一个或多个
? 0个或1个
* 0个或多个
# 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项
APPEND_SLASH=True
url(r'^blog/([0-9]{4})/\d{2}/$', views.blogs),
url地址上捕获的参数会按照 位置传参 方式传递给视图函数
def blogs(request,year):
url(r'^blog/(?P[0-9]{4})/(?P\d{2})/$' , views.blogs),
url地址上捕获的参数会按照 关键字传参 方式传递给视图函数
def blogs(request,year,month):
ROOT_URLCONF
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls')),
]
app01/urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^blog/$', views.blog), # /app01/blog/
url(r'^blog/(?P[0-9]{4})/(?P\d{2})/$' , views.blogs,),
]
静态路由
命名:
url(r'^blog/$', views.blog,name='blog'), # /app01/blog/ ——》 blog
反向解析:
模板
{% url 'blog' %} ——》 /app01/blog/
py:
from django.shortcuts import reverse
from django.urls import reverse
reverse('blog') # ——》 /app01/blog/
url(r'^blog/([0-9]{4})/(\d{2})/$', views.blogs,name='blogs'),
反向解析:
模板
{% url 'blogs' '2020' '02' %} ——》 /app01/blog/2020/02/
py
reverse('blogs',args=('2018','08') ——》 /app01/blog/2018/08/
url(r'^blog/([0-9]{4})/(\d{2})/$', views.blogs,name='blogs'),
反向解析:
模板
{% url 'blogs' '2020' '02' %} ——》 /app01/blog/2020/02/
{% url 'blogs' month='02' year='2020' %} ——》 /app01/blog/2020/02/
py
reverse('blogs',args=('2018','08') ——》 /app01/blog/2018/08/
reverse('blogs',kwargs={'year': '2018', 'month': '12'}) ——》 /app01/blog/2018/12/
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app01/', include('app01.urls', namespace='app01')),
url(r'^app02/', include('app02.urls', namespace='app02')),
]
反向解析
reverse('namespace:name',args=('2018','08') ——》 /app01/blog/2018/08/
{% url namespace:name %}
AutoField 自增字段 primary_key=True 变成主键
IntegerField 整形 10位 -2147483648 ~ 2147483647。
CharField 字符串 varchar max_length 必填参数
DateField
DatetimeField # auto_now_add=True 新增数据时自动保存当前的时间
# auto_now=True 新增和编辑数据时自动保存当前的时间
BooleanField 布尔类型
TextField 大文本
FloatField 浮点型
DecimalField 10进制小数 # 999.99
# max_digits,小数总长度 5
# decimal_places,小数位长度 2
自定义一个char类型
class MyCharField(models.Field):
"""
自定义的char类型的字段类
"""
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
def db_type(self, connection):
"""
限定生成数据库表的字段类型为char,长度为max_length指定的值
"""
return 'char(%s)' % self.max_length
字段参数
null=True 该字段在数据库可以为空
blank=True 允许用户输入为空
db_column 数据库中字段的列名
default 默认值
db_index 建立索引
unique 唯一约束
verbose_name 显示的字段名
choices 用户选择的参数
表的参数
class Person(models.Model):
pid = models.AutoField(primary_key=True) # 主键
name = models.CharField(verbose_name='姓名', unique=True, db_column='nick',
max_length=32, null=True, blank=True) # varchar(32)
age = models.IntegerField(default=18)
phone = MyCharField(max_length=11, unique=True)
gender = models.BooleanField(choices=((True, 'male'), (False, 'female')))
birth = models.DateTimeField(auto_now=True)
# auto_now_add=True 新增数据时自动保存当前的时间
# auto_now=True 新增和编辑数据时自动保存当前的时间
def __str__(self):
return "{}-{}".format(self.name, self.age)
class Meta:
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "person"
# admin中显示的表名称
verbose_name = '个人信息'
# verbose_name加s
verbose_name_plural = '所有用户信息'
# 联合索引
index_together = [
("name", "age"), # 应为两个存在的字段
]
#
# # 联合唯一索引
unique_together = (("name", "age"),) # 应为两个存在的字段
创建一个超级用户
python manage.py createsuperuser
在app下的admin.pu中注册model
from django.contrib import admin
from app01 import models
# Register your models here.
admin.site.register(models.Person)
登录http://127.0.0.1:8000/admin/操作表
https://www.cnblogs.com/alex3174/p/11608374.html
返回对象列表
all 查询所有的数据
filter 查询所有满足条件的数据
exclude 查询所有不满足条件的数据
values 查询数据的字段和值 [ {}]
values_list 查询数据的值 [ () ]
order_by 排序 默认升序 - 多字段排序 age id
reverse 对已经排好序的queryset翻转
distinct 去重
返回对象
get 获取有且唯一的对象
first 第一个
last 最后一个
返回布尔值
exists
数字
count
# 限制结果集 不能用负的下标
# 切片 取前两条记录
# data = User.objects.order_by('username')[:2]
# 从下标为4记录到下标为9的记录
# data = User.objects.order_by('uid')[4:10]
单表的双下划线
ret = models.Person.objects.filter(pid__lt=6) # 字段__条件 = less than 小于
ret = models.Person.objects.filter(pid__gt=6) # 字段__条件 = greater than 大于
ret = models.Person.objects.filter(pid__lte=6) # 字段__条件 = less than equal 小于等于
ret = models.Person.objects.filter(pid__gte=6) # 字段__条件 = greater than equal 大于等于
ret = models.Person.objects.filter(pid__range=[1,6]) # 范围
ret = models.Person.objects.filter(pid__in=[1,5,6]) # 成员判断
ret = models.Person.objects.filter(name__contains='alex') # like
ret = models.Person.objects.filter(name__icontains='alex') # like ignore忽略 忽略大小写
ret = models.Person.objects.filter(name__startswith='aaa') # 以什么开头
ret = models.Person.objects.filter(name__istartswith='aaa') # 以什么开头 忽略大小写
ret = models.Person.objects.filter(name__endswith='aaa') # 以什么结尾
ret = models.Person.objects.filter(name__iendswith='aaa') # 以什么结尾 忽略大小写
ret = models.Person.objects.filter(birth__year='2019')
ret = models.Person.objects.filter(birth__contains='-01-')
ret = models.Person.objects.filter(name__isnull=True) # 是否为null
外键的操作
class Publisher(models.Model):
name = models.CharField(max_length=32)
class Book(models.Model):
name = models.CharField(max_length=32)
pub = models.ForeginKey('Publisher',on_delete=models.CASCADE,related_name='books') # pub_id
# 基于对象的查询
book_obj = Book.objects.get(pk=1)
# 正向查询
book_obj.pub # 所关联的对象
book_obj.pub_id # 所关联的对象的id
# 反向查询
pub_obj = Publisher.objects.get(pk=1)
# 不指定related_name 使用 类名小写_set
pub_obj.book_set # 关系管理对象
pub_obj.book_set.all() # 所关联的所有的书籍对象
# 指定related_name='books'
pub_obj.books.all()
# 基于字段的查询
ret = Book.objects.filter(name='xxxx')
ret = Book.objects.filter(pub__name='出版社')
ret = Publisher.objects.filter(name='出版社')
# 不指定related_name
ret = Publisher.objects.filter(book__name='xxx')
# 指定related_name='books'
ret = Publisher.objects.filter(books__name='xxx')
# 指定related_query_name='xxx'
ret = Publisher.objects.filter(xxx__name='xxx')
################### 基于对象的查询 ####################
# 正向查询
book_obj = models.Book.objects.get(pk=1)
# print(book_obj.pub) # 关联的出版社对象
# print(book_obj.pub_id) # 关联的出版社的id
# 反向查询
pub_obj = models.Publisher.objects.get(pk=1)
# 没有指定related_name 类名小写_set
# print(pub_obj)
# print(pub_obj.book_set,type(pub_obj.book_set)) # 类名小写_set 关系管理对象
# print(pub_obj.book_set.all())
# 指定related_name='books' 没有 类名小写_set 的写法
################### 基于字段的查询 ####################
ret = models.Book.objects.filter(pub__name='新华出版社')
# 不指定related_name='books' 类名小写
# ret = models.Publisher.objects.filter(book__name='坐在床上学xx')
# 指定related_name='books' 不指定related_query_name
# ret = models.Publisher.objects.filter(books__name='坐在床上学xx')
# 指定related_query_name='book'
# ret = models.Publisher.objects.filter(book__name='坐在床上学xx')
多对多的操作
author_obj = models.Author.objects.get(pk=1)
# 关系管理对象的方法
# all 查询所有的对象
# set 设置多对多的关系 [id,id] [对象,对象]
# author_obj.books.set([1,2])
# author_obj.books.set(models.Book.objects.filter(id__in=[3, 4]))
# add 添加多对多关系 id或者对象
# author_obj.books.add(1, 2)
# author_obj.books.add(*models.Book.objects.filter(id__in=[5, 6]))
# remove 删除多对多关系 id或者对象
# author_obj.books.remove(1, 2)
# author_obj.books.remove(*models.Book.objects.filter(id__in=[5, 6]))
# clear 清空多对多关系
# author_obj.books.clear()
# create 新建一个对象和当前的对象建立关系
# author_obj.books.create(name='天然懵逼了没',pub_id=1)
ret = book_obj.authors.create(name='天然')
################### 外键 ###################
# set add create 参数只能写对象 不能写id
# pub_obj.books.set(models.Book.objects.filter(id__in=[1,2,3,4] ))
pub_obj.books.add(*models.Book.objects.all())
# remove clear 外键字段参数 null=True 才有这两个方法
# pub_obj.books.clear()
class Book(models.Model):
name = models.CharField(max_length=32)
pub = models.ForeginKey('Publisher',on_delete=models.CASCADE,related_name='books') # pub_id
class Author(models.Model):
name = models.CharField(max_length=32)
books = models.ManyToMantField('Book')
author_obj.books # 管理管理对象
# all 查询所有关联的对象
author_obj.books.all()
# set 设置关系 [1,2] [对象,对象]
author_obj.books.set([1,2]) # []
# add 新增关系 id 对象
author_obj.books.add(1,2)
# remove 删除关系 id 对象
author_obj.books.remove(1,2)
# clear 清空关系
author_obj.books.clear()
# create 新增一个所关联的对象,并且和当前的对象绑定关系
author_obj.books.create(name='xxx',pub_id='1')
# 一对多关系中 反向查询获取到的关系管理对象
# 参数中只能使用对象,不能使用id
# 当ForeginKey中有参数null=True时,才有remove、clear方法
from django.db.models import Max, Min, Count, Sum, Avg
# 聚合 aggregate 终止子句
ret = models.Book.objects.filter(id__gt=2).aggregate(min=Min('price'),max=Max('price'))
# 分组 group by
# annotate 注释 过程中使用了分组
# 1. 统计每一本书的作者个数
ret = models.Book.objects.annotate(Count('authors')).values() # 添加额外的信息
# 2. 统计出每个出版社卖的最便宜的书的价格
# 方法一
ret = models.Publisher.objects.annotate(Min('book__price')).values()
# 方法二
########## 错误写法 ##########
# ret = models.Book.objects.annotate(Min('price')).values() 按照书分组
########## 正确写法 ##########
# 按照 pub_id pub__name 分组
ret = models.Book.objects.values('pub','pub__name').annotate(min=Min('price'))
# 3. 统计不止一个作者的图书
ret = models.Book.objects.annotate(count=Count('authors')).filter(count__gt=1)
# 4. 根据一本图书作者数量的多少对查询集 QuerySet进行排序
ret = models.Book.objects.annotate(count=Count('authors')).order_by('-count')
# 5.查询各个作者出的书的总价格
ret = models.Author.objects.annotate(sum=Sum('books__price')).values()
ret = models.Book.objects.values('authors','authors__name').annotate(sum=Sum('price'))
F和Q
from django.db.models import F, Q
ret = models.Book.objects.filter(kucun__lt=50)
ret = models.Book.objects.filter(sale__gt=F('kucun')) # where 'sale' > 'kucun'
# ret = models.Book.objects.filter(id__lte=3).update(sale=F('sale') * 2 + 13)
# Q()
# | 或
# & 与
# ~ 非
ret = models.Book.objects.filter(Q(id__lt=3) | Q(id__gt=5))
ret = models.Book.objects.filter(Q(Q(id__lt=3) | Q(id__gt=5))&Q(name__startswith='天然'))
ret = models.Book.objects.filter(Q(Q(id__lt=3) | Q(id__gt=5))&~Q(name__startswith='天然'))
事务
from app01 import models
from django.db.models import F
from django.db import transaction
try:
with transaction.atomic():
# 一系列的操作
models.Book.objects.all().update(kucun=F('kucun') - 10)
models.Book.objects.all().update(sale=F('sale') + 10)
except Exception as e:
print(e)
保存在浏览本地上的一组组键值对
Http协议是无状态协议,每次请求之间都是相互独立,没有办法保存状态。使用cookie保存状态。
特性:
过程
第一个请求,没有cookie,设置键值对 ,根据浏览器生成一个唯一标识,给一个字典设置键值对。
将字典转成字符串(json序列化),进行加密,将唯一标识和字符串保存在数据库中(django_sessoion)。
3. 返回给浏览器唯一标识(sessionid)的cookie
4. 下次请求携带sessionid,服务器根据session找到对应的数据(session_data),进行解密,进行反序列化,根据key获取对应的值。
django中操作cookie
# 设置cookie:
response.set_cookie(key,value) # Set-Cookie: is_login=1
response.set_signed_cookie('key', value, salt='s28') # 加密cookie
设置cookie的参数
# max_age 超时时间
# path cookie生效的路径
# secure=True https进行传输
# httponly=True 只能传输,无法被JavaScript获取
# 获取cookie
request.COOKIES {} # 请求头 Cookie: is_login=1;
request.get_signed_cookie('key', salt='s28',defalut='') # 加密cookie
# 删除cookie 设置cookie 值为空 超时时间为0
response.delete_cookie(key)
# 设置cookie 响应头 Set-cookie
ret = HttpResponse('xx')
ret.set_cookie(key,value,max_age=5,path='/',) # ret['Set-cookie'] = 'key:value;'
ret.set_signed_cookie(key,value,salt='',max_age=5,path='/',)
# max_age 超时时间
# path 生效的路径
# 获取cookie cookie的请求头
request.COOKIES {} [key] .get(key)
request.get_signed_cookie(key,salt='',default='')
# 删除cookie Set-cookie key:'' max_age=0
ret = HttpResponse('xx')
ret.delete_cookie(key)
保存在服务器上的一组组键值对,依赖于cookie使用
为什么要使用session?
1. cookie是保存在浏览器本地,不太安全
2. 浏览器会对cookie的大小和个数有一定限制的
django中操作session
# 设置session
request.session[key] = value
# 获取
request.session[key] request.session.get(key)
# 删除
del request.session[key]
request.session.pop(key)
# session_key
request.session.session_key
# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()
# 删除当前用户的所有的session数据
request.session.delete() # 不删除cookie
request.session.flush() # 删除cookie
# 设置会话Session和Cookie的超时时间
request.session.set_expiry(value)
###############################
# 设置session
reqeust.session[key] = value
# 获取
reqeust.session[key]
reqeust.session.get(key)
# 删除
request.session.pop(key) del reqeust.session[key]
request.session.delete() # 删除所有的键值对 不删除cookie
request.session.flush() # 删除所有的键值对 也删除cookie
# 其他
默认的超时时间(2周)
request.session.set_expiry(value) # 设置超时时间
request.session.clear_expired() # 清除已经失效的session数据
from django.conf import global_settings
SESSION_COOKIE_NAME = 'sessionid'
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
SESSION_SAVE_EVERY_REQUEST = True # 每次请求都更新session数据
SESSION_EXPIRE_AT_BROWSER_CLOSE = True # cookie在浏览器关闭时就失效
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 默认是数据库 文件 缓存 数据库+缓存 加密cookie
配置
from django.conf import global_settings
SESSION_COOKIE_NAME = 'session'
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
SESSION_SAVE_EVERY_REQUEST = True # 每次请求后更新超时时间
SESSION_EXPIRE_AT_BROWSER_CLOSE = True # 关闭浏览器cookie就失效
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
# session 保存的位置
# 默认是数据库 缓存 缓存+数据库 文件 加密cookie
from django.contrib.sessions.backends import db
Django中处理请求和相应的框架级别的钩子。本质上就是一个类,类定义5个方法,特定时执行这些方法。
# 定义
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self,request):
pass
# settings中要注册
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'app01.middlewares.my_middleware.MD1',
'app01.middlewares.my_middleware.MD2',
]
5个方法,4个特征
执行时间 执行顺序 参数 返回值
执行时间: 视图函数之前
参数:
request 请求的对象 和 视图函数是同一个对象
执行顺序:
按照注册的顺序 顺序执行
返回值:
None 正常流程
HttpResponse 不执行后续的中间件的process_request、路由匹配、process_view、视图都不执行,直接执行当前中间的process_response方法
执行时间: 视图函数之后
参数:
request 请求的对象 和 视图函数是同一个对象
response 响应对象
执行顺序:
按照注册的顺序 倒序执行
返回值:
HttpResponse 必须返回
执行时间: 路由匹配之后,视图函数之前
参数:
request 请求的对象 和 视图函数是同一个对象
view_func 视图函数
view_args 视图函数的位置参数
view_kwargs 视图函数的关键字参数
执行顺序:
按照注册的顺序 顺序执行
返回值:
None 正常流程
HttpResponse 之后中间件的process_view、视图都不执行,执行最后一个中间件的process_response方法
执行时间(触发条件): 视图中有异常才执行
参数:
request 请求的对象 和 视图函数是同一个对象
exception 异常的对象
执行顺序:
按照注册的顺序 倒序执行
返回值:
None 当前的中间件没有处理异常,交给下一个中间件处理异常,如果都没有处理异常,django处理异常
HttpResponse 当前中间件处理了异常,后面的中间件的process_exception就不执行,执行最后一个中间件的process_response方法
执行时间(触发条件): 视图中返回的对象是TemplateResponse对象
参数:
request 请求的对象 和 视图函数是同一个对象
response TemplateResponse的对象
执行顺序:
按照注册的顺序 倒序执行
返回值:
HttpResponse TemplateResponse的对象
过程处理模板的名字 参数
response.template_name
response.context_data
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wq1vQ5DM-1653464368408)(C:/Users/hp/AppData/Local/Temp/360zip$Temp/360(0/assets/1168194-20180719084357413-1778333372.png)]
轻量级的文本数据交换格式
python
支持的数据类型:
字符串 数字 布尔值 列表 字典 None
序列化
python的数据类型 ——》 json字符串
反序列话
json字符串 ——》 python的数据类型
ajax 是一个js的技术,异步发送请求的。
ajax特点:
1. 异步
2. 局部刷新
3. 传输的数据量小
简单使用
$.ajax({
url:'/calc/',
type:'get',
data:{
'x1':$('[name="i1"]').val(),
'x2':$('[name="i2"]').val(),
},
success:function (data) {
$('[name="i3"]').val(data)
}
})
ajax传收数据:
$.ajax({
url: '/test/',
type: 'get',
data: {
name: 'alex',
age: 84,
hobby: JSON.stringify(['抽烟', '喝酒', '画大饼'])
},
success: function (data) {
console.log(data)
console.log(data.status)
}
})
使用ajax上传文件
$('#b1').click(function () {
var formData = new FormData(); // multipart/form-data
formData.append('name', 'alex');
formData.append('f1', $('#f1')[0].files[0]);
{#formData.append('f1',document.getElementById('f1').files[0]);#}
$.ajax({
url: '/upload/',
type: 'post',
data: formData,
processData: false, // ajax不处理数据的编码
contentType: false, // 不修改content-type的请求头
success: function (data) {
alert(data)
}
})
})
from django.views.decorators.csrf import csrf_exempt, csrf_protect,ensure_csrf_cookie
csrf_exempt 加在视图上 该视图不需要进行csrf校验
csrf_protect 加在视图上 该视图需要进行csrf校验
ensure_csrf_cookie 加在视图上 确保返回时设置csrftoken的cookie
从cookie中获取csrftoken的值
从request.POST中获取csrfmiddlewaretoken的值或者从请求头中获取x-csrftoken的值
把这两个值做对比,对比成功就接受请求,反之拒绝
前提:必须有csrftoken的cookie
使用{% csrf_token %}
使用ensure_csrf_cookie的装饰器,加在视图上
from django.views.decorators.csrf import ensure_csrf_cookie
让ajax可以通过django的csrf的校验:
推荐的方式:导入文件 + 使用{% csrf_token %}
csrf的中间件的流程:
process_request
从cookie中获取了csrftoken的值,将该值放入request.META中
process_view
判断视图是否使用了csrf_exempt的装饰器,使用了就不再进行csrf 的校验了
判断请求方式是否是get head potions trace:
如果是 直接接受该请求 不做csrf的校验
不是 这些请求 要进行csrf的校验:
csrf_token = request.META.get(‘CSRF_COOKIE’) # cookie中csrftoken的值
request_csrf_token = ‘’
请求方式是post,尝试从request.POST中获取csrfmiddlewaretoken的值
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
如果request_csrf_token = ‘’,再尝试从请求头中获取X-CSRFTOKEN的值
request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')
比较csrf_token和request_csrf_token
使用:
导入jquery.js
<script>
var v1 = 'v1'
$.ajax({
url:'地址',
type:'post', // 请求方式
headers:{
x-csrftoken:'asdasdkljlqjklqewqweweqwe'
},
data:{
k1: v1,
k2:'v2',
// csrfmiddlewaretoken:'xasdaskklqjwkleqweqweqweqweqwe'
},
success:function(data) { // data 是返回响应的响应体
// 后续的操作
}
})
</script>
上传文件
html
form组件定义
class RegForm(forms.Form):
user = forms.CharField(label='用户名')
pwd = forms.CharField(label='密码')
使用:
def reg2(request):
form_obj = RegForm() # 空的form
if request.method == 'POST':
# 对提交的数据做校验
form_obj = RegForm(request.POST) # 包含用户提交的数据的form
if form_obj.is_valid(): # 对数据进行校验
# 校验成功
return HttpResponse('注册成功')
return render(request, 'reg2.html', {'form_obj': form_obj})
模板:
{{ form_obj.as_p }} 一次性生成所有的input框
{{ form_obj.user }} 该字段的input框
{{ form_obj.user.label }} 该字段提示的信息
{{ form_obj.user.id_for_label }} 该字段的input框的id
{{ form_obj.user.errors }} 该字段的所有错误
{{ form_obj.user.errors.0 }} 该字段的第一个错误
{{ form_obj.errors }} 所有字段的错误
常用字段
CharField 文本输入框
ChoiceField 单选 默认是select
MultipleChoiceField 多选 默认是select
字段参数
initial 默认值
choices 用户选择的数据
error_messages 自定义错误信息
widget 插件 修改input框的类型
required 是否必填
disabled 是否禁用
完整使用
# 定义form
form django import forms
class RegForm(forms.Form):
user = forms.CharField(
label='用户名',
min_length=11,
inital='初始值',
required=True,
disabled=False,
error_messages={
'required':'xxxx',
'min_length':'xxssw'
}
validators=[自定义的函数,内置的校验器]
)
password = forms.CharField(widget=forms.PasswordInput)
url(^'reg',reg)
def reg(request):
form_obj = RegForm()
if request.method == 'POST':
form_obj = RegForm(request.POST)
# 对提交的数据进行校验
if form_obj.is_valid():
# 校验成功做的操作
form_obj.cleaned_data # request.POST
return render(request,'reg.html',{'form_obj':form_obj})
校验
校验器:
定义函数
def check_name(value):
# 不符合校验规则
if 'alex' in value:
raise ValidationError('alex的名字不符合社会主义价值观')
# 符合校验不做任何操作
user = forms.CharField(
...
validators=[check_name],)
#########################################
from django.core.exceptions import ValidationError
def xxx(value):
# 写校验规则
# 校验成功 什么都不做 return
# 校验失败 抛出异常 ValidationError('提示信息')
使用内置的校验器
from django.core.validators import RegexValidator
phone = forms.CharField(validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式不正确')])
钩子函数:
局部钩子
# 一定要写在类中
def clean_user(self):
# 不符合校验规则 抛出异常
value = self.cleaned_data.get('user')
if 'alex' in value:
raise ValidationError('alex的名字不符合社会主义价值观')
# 符合校验规则 返回该字段的值
return value
###########################
class RegForm(forms.Form):
user = forms.CharField()
def clean_user(self):
# 校验
# 校验成功 返回该字段的值
# 校验失败 抛出异常
全局钩子
def clean(self):
# 全局钩子
pwd = self.cleaned_data.get('pwd')
re_pwd = self.cleaned_data.get('re_pwd')
if pwd != re_pwd:
# 不符合校验规则 抛出异常
# 将错误信息加入到某个字段中
self.add_error('re_pwd','两次密码不一致!!!')
raise ValidationError('两次密码不一致')
# 符合校验规则 返回所有字段的值
return self.cleaned_data
###############################
class RegForm(forms.Form):
user = forms.CharField()
def clean(self):
# 校验
# 校验成功 返回所有字段的值 self.cleaned_data
# 校验失败 抛出异常
is_valid()的流程:
执行一个full_clean方法
定义一个 错误字典 和 cleaned_data = {} # 存在已经经过校验的数据的字典、
执行self._clean_fields()
循环所有的字段
对一个字段进行内置的校验、校验器的校验、局部钩子的校验
校验不通过,错误字典中有该字段的错误信息
所有校验通过,self.cleaned_data有该字段的值
执行全局钩子
定义的modelform
from django import forms
class RegForm(forms.ModelForm):
# username = forms.CharField()class RegForm(forms.ModelForm):
# username = forms.CharField(label='xxx')
# password = forms.CharField()
password = forms.CharField(error_messages={'required': '这是必选项'}, widget=forms.PasswordInput, label='密码',
min_length=6)
re_pwd = forms.CharField(widget=forms.PasswordInput, label='确认密码', min_length=6)
class Meta:
model = models.User
fields = '__all__' # ['username','password']
exclude = ['last_time']
# labels = {
# 'username': '用户名'
# }
widgets = {
'password': forms.PasswordInput
}
error_messages = {
'username': {
'required': '必填项',
},
'password': {
'required': '必填项',
}
}
def clean_phone(self):
import re
phone = self.cleaned_data.get('phone')
if re.match(r'^1[3-9]\d{9}$', phone):
return phone
raise ValidationError('手机号格式不正确')
def clean(self):
self._validate_unique = True # 数据库校验唯一
password = self.cleaned_data.get('password')
re_pwd = self.cleaned_data.get('re_pwd')
if password == re_pwd:
return self.cleaned_data
self.add_error('re_pwd', '两次密码不一致!!')
raise ValidationError('两次密码不一致')
# models.py
class User(models.Model):
"""
员工信息表用户、密码、职位、公司名(子、总公司)、手机、最后一次登录时间。
"""
username = models.CharField(max_length=32, verbose_name='用户名',unique=True)
password = models.CharField(max_length=32, verbose_name='密码')
position = models.CharField(max_length=32, verbose_name='职位')
company = models.CharField(max_length=32, verbose_name='公司',
choices=(('0', '北京总公司'), ('1', '石家庄分公司'), ('2', '广州分公司')))
phone = models.CharField(max_length=11, verbose_name='手机号')
last_time = models.DateTimeField(null=True, blank=True, verbose_name='上次登陆时间')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='注册时间')
form
from django import forms
from app01 import models
# form
class RegForm(forms.Form):
username = forms.Charfield(label='用户名')
password = forms.Charfield(widget=forms.PasswordInput)
# modelform
class RegForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput, label='密码',min_length=6)
re_pwd = forms.CharField(widget=forms.PasswordInput, label='确认密码', min_length=6)
class Meta:
model = models.User
fields = '__all__' # ['username','password']
exclude = ['last_time']
labels = {
'username':'用户名'
}
widgets = {
'password':forms.PasswordInput
}
error_messages = {
'username':{}
}
def clean_password(self):
password = self.cleaned_data.get('password')
# 通过校验 返回该字段的值
# 不通过校验 抛出异常ValidationError
def clean(self):
self._validate_unique = True # 校验数据的唯一性
# 通过校验 返回所有字段的值 self.cleaned_data
# 不通过校验 抛出异常ValidationError
password = self.cleaned_data.get('password')
re_pwd = self.cleaned_data.get('re_pwd')
if re_pwd == password:
return self.cleaned_data
self.add_error('re_pwd','两次密码不一致')
raise ValidationError('两次密码不一致') # '__all__'
def reg(request,*args,**kwargs):
form_obj = RegForm()
if request.method == 'POST':
form_obj = RegForm(request.POST) # 包含用户提交的数据
if form_obj.is_valid(): # 对数据进行校验
form_obj.save() # 新增上数据
return redirect('login')
return render(request,'reg.html',{'form_obj':form_obj})
# html
{{ form_obj.username }} input框
{{ form_obj.username.label }} label提示
{{ form_obj.username.id_for_label }} input框的id
{{ form_obj.username.errors.0 }}
需求:
博客的首页
文章详情页 3
后台的管理 2
分类管理 2.1
文章管理 2.2
展示数据:
给模板一个字querySet 对象列表,循环出对象obj。
普通字段
obj.字段名 ——》 数据库中的数据
外键
obj.外键 ——》 外键的对象 给类定义__str__
的方法
obj.外键.字段
带choices参数的
obj.字段名 ——》 数据库中的数据 这个显示不是很好
obj.get_字段名_display()
显示定义好的结果
自定义方法
def show_publish_status(self):
color_dict = {True: 'green', False: '#c35353'}
return mark_safe(
'{}'.format(color_dict[self.publish_status],self.get_publish_status_display()))
展示数据的方法:
数据对象 obj
普通字段
obj.字段名 ——》 数据库该字段的值
带choices参数的
obj.字段名 ——》 数据库该字段的值
obj.get_字段名_display()
要显示的结果
外键
obj.外键 ——》 所关联的对象 __str__
方法
obj.外键.字段
自定义方法
from django.utils.safestring import mark_safe
def show_publish_status(self):
color_dict = {True: 'green', False: '#c35353'}
return mark_safe(
'{}'.format(color_dict[self.publish_status],
self.get_publish_status_display()))
定义modelform
class ArticleForm(forms.ModelForm):
class Meta:
model = models.Article
fields = "__all__"
exclude = ['detail']
# widgets = {
# 'title': forms.TextInput(attrs={'class': 'form-control'})
# }
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 自定义的操作
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
使用form
def article_add(request):
form_obj = ArticleForm()
if request.method == 'POST':
form_obj = ArticleForm(request.POST)
if form_obj.is_valid():
# 获取文章详情的字符串
detail = request.POST.get('detail')
# 创建文章详情的对象
detail_obj = models.ArticleDetail.objects.create(content=detail)
form_obj.instance.detail_id = detail_obj.pk
form_obj.save() # form_obj.instance.save()
return redirect('article_list')
return render(request, 'article_add.html', {'form_obj': form_obj})
# 编辑文章
def article_edit(request, pk):
obj = models.Article.objects.filter(pk=pk).first()
form_obj = ArticleForm(instance=obj)
if request.method == 'POST':
form_obj = ArticleForm(request.POST, instance=obj)
if form_obj.is_valid():
form_obj.instance.detail.content = request.POST.get('detail')
form_obj.instance.detail.save() # 保存文章详情
form_obj.save() # 保存文章的信息
return redirect('article_list')
return render(request, 'article_edit.html', {'form_obj': form_obj, 'obj': obj})
# HTML
{% for field in form_obj %}
{{ field.id_for_label }}
{{ field.label }}
{{ field }} input框
{{ field.errors.0 }}
{% endfor %}
让用户上传头像
model中添加ImageField
avatar = models.ImageField(upload_to='img/avatar',default='img/avatar/dafault.jpeg')
# ImageField 依赖 pillow模块 pip install pillow
media的配置
seetings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
urls.py
from django.views.static import serve
from django.conf import settings
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include('app01.urls')),
url(r'^media/(?P.*)' , serve, {'document_root': settings.MEDIA_ROOT}),
]
使用django-ckeditor
https://pypi.org/project/django-ckeditor/
下载
pip install django-ckeditor
注册app
INSTALLED_APPS = [
...
'ckeditor',
'ckeditor_uploader',
]
urls.py
urlpatterns = [
...
url(r'^ckeditor/', include('ckeditor_uploader.urls')),
]
models.py
class ArticleDetail(models.Model):
"""
文章详情
"""
content = RichTextUploadingField(verbose_name='文章内容')
modelform
class ArticleDetailForm(forms.ModelForm):
class Meta:
model = models.ArticleDetail
fields = "__all__"
form_obj = ArticleDetailForm()
html
{{ form_obj. content }}
引入静态文件
{% load static %}
上传文件需要认证,取消认证
# 将staff_member_required装饰器取消掉即可
urlpatterns = [
url(r'^upload/', staff_member_required(views.upload), name='ckeditor_upload'),
url(r'^browse/', never_cache(staff_member_required(views.browse)), name='ckeditor_browse'),
]
路由
url(r'^category_add/$', views.category_change, name='category_add'),
url(r'^category_edit/(\d+)$', views.category_change, name='category_edit'),
视图
def category_change(request, pk=None):
obj = models.Category.objects.filter(pk=pk).first() # pk=None obj=>None
form_obj = CategoryForm(instance=obj)
if request.method == 'POST':
form_obj = CategoryForm(request.POST, instance=obj)
if form_obj.is_valid():
form_obj.save()
return redirect('category_list')
title = '编辑分类' if pk else '新增分类'
return render(request, 'form.html', {'form_obj': form_obj, 'title': title})
模板
{% extends 'dashboard.html' %}
{% block main %}
{{ title }}
{% endblock %}
Q
q = Q()
# Q(Q(title__contains=query)|Q(detail__content__contains=query))
q.connector = 'OR'
q.children.append(Q(title__contains=query))
q.children.append(Q(detail__content__contains=query))
Q(title__contains=query) # Q(('title__contains',query))
def get_query(request, field_list):
# 传入一个列表['title','detail__content'] ,返回一Q对象
query = request.GET.get('query', '')
q = Q()
# Q(Q(title__contains=query)|Q(detail__content__contains=query))
q.connector = 'OR'
for field in field_list:
q.children.append(Q(('{}__contains'.format(field),query)))
# q.children.append(Q(detail__content__contains=query))
return q
from django.http.request import QueryDict
request.GET._mutable = True
request.GET['page'] = 1
request.GET.copy() # 返回一个可编辑的深拷贝
request.GET.urledncode() # page=1&aa=111
展示数据的4个方法
对单标的新增和编辑
两个url + 一个视图(modelform) + 一个模板
评论框样式
http://www.jq22.com/demo/jQueryWbPl201705260102/
文章详情 模板
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Blog Template for Bootstraptitle>
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
{% load static %}
<link href="{% static 'css/blog.css' %}" rel="stylesheet">
<link href="{% static 'css/comment/comment.css' %}" rel="stylesheet">
<link href="{% static 'css/comment/style.css' %}" rel="stylesheet">
head>
<body>
{% include 'nav.html' %}
<div class="container">
<div class="blog-header">
<h1>{{ article_obj.title }}h1>
<p class="lead blog-description">{{ article_obj.detail.content|safe }}p>
<div class="commentAll">
<div class="reviewArea clearfix">
<div class="flex-text-wrap">
<pre class="pre"><span>span><br>pre>
<textarea class="content comment-input" placeholder="Please enter a comment…"
>textarea>div>
<a href="javascript:;" id='comment' class="plBtn">评论a>
div>
<div class="comment-show">
{% for comment in article_obj.comment_set.all %}
<div class="comment-show-con clearfix">
<div class="comment-show-con-img pull-left">
<img src="{{ comment.author.avatar.url }}" alt="">
div>
<div class="comment-show-con-list pull-left clearfix">
<div class="pl-text clearfix">
<a href="#" class="comment-size-name">{{ comment.author.username }} : a>
<span class="my-pl-con"> {{ comment.content }}span>
div>
<div class="date-dz">
<span class="date-dz-left pull-left comment-time">{{ comment.time }}span>
<div class="date-dz-right pull-right comment-pl-block">
<a href="javascript:;" class="removeBlock">删除a>
div>
div>
<div class="hf-list-con">div>
div>
div>
{% endfor %}
div>
div>
div>
div>
<footer class="blog-footer">
<p>Blog template built for <a href="http://getbootstrap.com">Bootstrapa> by <a
href="https://twitter.com/mdo">@mdoa>.p>
<p>
<a href="#">Back to topa>
p>
footer>
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js">script>
<script>window.jQuery || document.write('