工作流程
Django开发原则
官⽹⼿册介绍
步骤
为什么需要虚拟环境
1.创建项目
2.运行项目—manage.py入口文件,启动
3.迁移数据库—manage.py迁移
项目文件夹结构
├── 项目名
│ ├── __init__.py
│ ├── settings.py (项目配置文件)
│ ├── urls.py (项目路由)
│ └── wsgi.py (python网关)
├── manage.py (脚手架)
├── db.sqlite3 (数据库)
└── templates (模板目录)
manage.py:
settings.py:
urls.py:
wsgi.py:
app应用文件夹
project和app的关系
创建app
app中的⽂件:
__init__.py 说明⽬录是⼀个Python模块 ,作用:在这里做声明
models.py 写和数据库相关的内容
views.py 写视图函数。接收请求,处理数据 与M和T进⾏交互
tests.py 写测试用例。写测试代码的⽂件(暂时不需要关⼼)
admin.py 后台管理有关。⽹站后台管理相关的
应⽤注册
DEBUG模式
request对象
HttpResponseBase
对象或者⼦类的对象。-----定义应用news/views.py-----
from django.http import HttpResponse
def news(request):
return HttpResponse("新闻!")
-----注册应用urls.py------
from news import views
urlpatterns = [
path("news",views.news)
]
URL映射
作用
:
实现
:django.urls.path-----urls.py------
from django.contrib import admin
from django.urls import path
from book import views
urlpatterns = [
path('admin/', admin.site.urls),
path('book/',views.book_list)
]
re_path函数
:有时候我们在写url匹配的时候,想要写使⽤正则表达式来实现⼀些复杂的需 求,那么这时候我们可以使⽤re_path来实现。re_path的参数和path参数⼀模 ⼀样,只不过第⼀个参数也就是route参数可以为⼀个正则表达式。
模块化原因:
实现方法:include
------urls.py⽂件--------
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('book/',include("book.urls"))
##以后在请求book的app相关的url的时候都需要加⼀个book的前缀。
##这里的路由与app的路由拼接,最后形成最终的url
]
------book/urls.py⽂件--------
from django.urls import path
from . import views
urlpatterns = [
path('', views.index),
path('sign/', views.login)
]
作用
:
特点
:
参数传递的两种方法
:
URL映射--路由的传参---urls.py
'''
<>尖括号
'''
-----book/urls.py------
urlpatterns = [
path('/',views.book_detail)
re_path(r"(?P\d{4})/",views.article_list)
]
-----book/views.py-----
def book_detail(request,book_id):
text = "您输⼊的书籍的id是:%s" % book_id 14
return HttpResponse(text)
def current_year(request, year):
return HttpResponse('年份是%s' % year)
'''
?关键字参数
'''
-----urls.py------
from book import views
urlpatterns = [
path('admin/', admin.site.urls),
path('book/detail/',views.book_detail)
]
-----book/views.py-----
def book_detail(request):
book_id = request.GET.get("id")
text = "您输⼊的书籍id是:%s" % book_id
return HttpResponse(text)
URL反转传递参数
作用:
利用当前url,添加参数,可以是适用于重定向的返回的url改变实现:
为什么需要URL命名
:因为在项⽬开发的过程中URL地址可能经常变动,如果写死会经常去修改,指定了命名就可以修改视图函数了和视图函数的url如何给⼀个URL指定名称
:path(‘login/’, views.login) 改为path(‘signIn/’, views.login, name=“login”),这样我们并不需要因为改变了路由就需要将视图函数改变应⽤命名空间
:在多个app之间可能产⽣同名的URL,即 name是同样的,这时候为了避免这种情况,可以使⽤ 命名空间来加以区分。在urls.py中添加app_name即可------front\urls.py------
from django.urls import path
from . import views
# 这个就是应用命名空间
app_name = 'front'
urlpatterns = [
path('', views.index),
path('signIn/', views.login, name='login'),
]
------front\views.py------
from django.shortcuts import render, redirect
from django.http import HttpResponse
def index(request):
name = request.GET.get('name')
if name:
return HttpResponse('前台首页')
else:
return redirect('front:login')
def login(request):
return HttpResponse('前台登录页面')
实例命名空间
作用:
实现:
namespace=‘实例命名空间’"""
实例命名空间namespace
"""
------urls.py⽂件--------
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('cms1/', include("cms.urls",namespace='cms1')),
path('cms2/', include("cms.urls",namespace='cms2')),
]
------cms/views.py⽂件--------
current_namespace = request.resolver_match.namespace
print(current_namespace)
模板介绍
DTL与普通的HTML⽂件的区别
渲染模板步骤
首先:
模板选择(都需要配置)
------settings.py 中TEMPLATES--------
# DIRS优先级高于APP_DIRS。DIRS就是总模板。APP_DIRS就是开启应用模板
# 默认DIRS为[],应用模板开启,添加os.path.join(BASE_DIR, 'templates')就是说明会在总模板的文件夹中找
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
------settings.py 中TEMPLATES--------
# 默认APP_DIRS是开启的,但是应用还需要注册,总模板如果开启的话会先找它,因为优先级更大
# APP_DIRS:默认为True,这个设置为True后,会在INSTALLED_APPS的安 装了的APP下的templates⽂件加中查找模板。settings.py中 INSTALLED_APPS数组中添加你的app名字
'DIRS': [],
'APP_DIRS': True,
------settings.py 中INSTALLED_APPS-------
# 往列表中添加某应用名称
'book'
其次:
模板加载,方式两种:
from django.template.loader import render_to_string
from django.http import HttpResponse
def book_detail(request,book_id):
html = render_to_string("index.html")
return HttpResponse(html)
from django.shortcuts import render
def book_list(request):
return render(request,'index.html')
查找顺序:
模板传参
--------views.py--------
def profile(request):
return render(request,'profile.html',context={'username':'juran'})
class Person(object):
def __init__(self,username):
self.username = username
def index(request):
p = Person("居然")
content = { 'person':p }
return render(request,"index.html",context=content)
-------profile.html-----
{{ username }}
-------index.html-----
{{ person.username }}
常⽤的模板标签
{% for book in books %}
{{ book }}
{% endfor %}
--------反向遍历reversed------
{% for book in books reversed %}
{{ book }}
{% endfor %}
{% for key,value in person.items %}
key:{{ key }}
value:{{ value }}
{% endfor %}
forloop.counter:当前循环的下标。以1作为起始值。
forloop.counter0:当前循环的下标。以0作为起始值。
forloop.revcounter:当前循环的反向下标值。⽐如列表有5个元素,那么第⼀次遍历这 个属性是等于5,第⼆次是4,以此类推。并且是以1作为最后⼀个元素的下标。
forloop.revcounter0:类似于forloop.revcounter。不同的是最后⼀个元素的下标 是从0开始。
forloop.first:是否是第⼀次遍历。
forloop.last:是否是最后⼀次遍历。
forloop.parentloop:如果有多个循环嵌套,那么这个属性代表的是上⼀级的for循环
如果没有元素的情况下,会执⾏empty中的内容。
{% for person in persons %}
- {{ person }}
{% empty %}
暂时还没有任何⼈
{% endfor %}
break,continue语句是⽤不了的。
href属性
。
模板常⽤过滤器
定义:
将传进来的参数添加到原来的值上⾯。这个过滤器会尝试将值和参数int强转成整形,然后进⾏相加。如果强转失败了,那么会将值和参数进⾏拼接,直接相加。如果是字符串,那么会拼接成字符串,如果是列表,那么会拼接成⼀个列表。-------templates\index.html---------
{{ value|add:"2" }}
## 如果value是等于4,那么结果将是6。如果value是等于⼀个普通的字符串,⽐ 如abc,那么结果将是abc2
定义:
移除值中所有指定的字符串。类似于python中的replace(args,"")。-------templates\index.html---------
{{ value|cut:" " }}
定义:
将⼀个⽇期按照指定的格式,格式化成字符串。有默认的格式,可以修改成自己想要的-------templates\index.html---------
{{ birthday|date:"Y/m/d" }}
------views.py------------
from django.shortcuts import render
def book_list(request):
context = {"birthday": datetime.now()}
return render(request,'index.html',context=content)
定义:
如果值被评估为False。⽐如[],"",None,{}等这些在if判断中为False的值, 都会使⽤default过滤器提供的默认值。-------templates\index.html---------
{{ value|default:"nothing" }}
## 如果value是等于⼀个空的字符串。⽐如"",那么以上代码将会输出nothing。
定义:
返回列表/元组/字符串中的第⼀个元素/最后⼀个元素。-------templates\index.html---------
{{ value|first }}
{{ value|last }}
定义:
使⽤四舍五⼊的⽅式格式化⼀个浮点类型。如果这个过滤器没有传递任何参 数。那么只会在⼩数点后保留⼀个⼩数,如果⼩数后⾯全是0,那么只会保留整 数。当然也可以传递⼀个参数,标识具体要保留⼏个⼩数。-------templates\index.html---------
- {{ 34.32|floatformat }}
34.3 2
- {{ 34.35|floatformat }}
34.4 3
- {{ 34.353333|floatformat:3}}
34.353
定义:
类似与Python中的join,将列表/元组/字符串⽤指定的字符进⾏拼接.-------templates\index.html---------
{{ value|join:"/" }}
## 如果value是等于['a','b','c'],那么以上代码将输出a/b/c。
定义:
获取⼀个列表/元组/字符串/字典的⻓度。-------templates\index.html---------
{{ value|length }}
##如果value是等于['a','b','c'],那么以上代码将输出3。如果value为None,那么 以上将返回0
定义:
获取⼀个列表/元组/字符串/字典的⻓度。-------templates\index.html---------
{{ value|length }}
##如果value是等于['a','b','c'],那么以上代码将输出3。如果value为None,那么 以上将返回0
模版结构优化
--------header.html ------重复代码
我是header
⽤户名:{{ username }}
--------footer.html -----重复代码
我是footer
--------main.html -------调用重复代码、传参
{% include "header.html" with username='juran' %}
我是main内容
{% include 'footer.html' %}
开始的地⽅定义这个block的名字
,还可以在 block结束的时候定义名字
。⽐如{% block title %}{% endblock title %}。这 在⼤型模版中显得尤其有⽤,能让你快速的看到block包含在哪⾥。--------base.html ------重复代码
{% block title %}我的站点{% endblock %}
{% block content %}
{% endblock %}
--------------index.html----------继承并重写
{% extends "base.html" %}
{% block title %}博客列表{% endblock %}
{% block content %}
{% for entry in blog_entries %}
{{ entry.title }}
{{ entry.body }}
{% endfor %}
{% endblock %}
加载静态⽂件
STATICFILES_DIRS = [ os.path.join(BASE_DIR,"static") ]
{% load static %}
------settings.py-------TEMPLATES/OPTIONS
'builtins':['django.templatetags.static'],这样以后在模版中就可以直接使⽤static标 签,⽽不⽤⼿动的load了。
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# 其他的url映射
] + static(settings.STATIC_URL, document_root=settings.STATICFILES_DIRS[0])
```
--------------settings.py--------------
DATABASES = {
'default': {
# 数据库引擎(是mysql还是oracle等)
'ENGINE': 'django.db.backends.mysql',
# 数据库的名字
'NAME': 'logic',
# 连接mysql数据库的⽤户名
'USER': 'root',
# 连接mysql数据库的密码
'PASSWORD': 'root',
# mysql数据库的主机地址
'HOST': '127.0.0.1',
# mysql数据库的端⼝号
'PORT': '3306',
}
}
## 连接Linux服务器MySQL问题:https://blog.csdn.net/qq473179304/article/d etails/56665364
在Django中操作数据库
ORM模型介绍
settings.py的INSTALLED_APP
中进⾏安装。以下是写⼀个简单的书籍ORM模 型。-----------------------models.py----------------------------
from django.db import models
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=20,null=False)
author = models.CharField(max_length=20,null=False)
pub_time = models.DateTimeField(default=datetime.now)
price = models.FloatField(default=0)
# 查询到的数据可以直接
def __str__(self):
return 'id:%s, name:%s' % (self.id, self.name)
--- 以上便定义了⼀个模型。这个模型继承⾃django.db.models.Model,如果这个模型想要映射到数据库中,就必须继承⾃这个类。这个模型以后映射到数据库 中,表名是模型名称的⼩写形式,为book。
--- 在这个表中,有四个字段,⼀个为 name,这个字段是保存的是书的名称,是varchar类型,最⻓不能超过20个字 符,并且不能为空。
第三个字段是作者名字类型,同样也是varchar类型,⻓度 不能超过20个。
第四个是出版时间,数据类型是datetime类型,默认是保存这 本书籍的时间。
第五个是这本书的价格,是浮点类型。 还有⼀个字段我们没有写,就是主键id,在django中,如果⼀个模型没有定义 主键,那么将会⾃动⽣成⼀个⾃动增⻓的int类型的主键,并且这个主键的名字 就叫做id。
模型常⽤字段
必须要传递max_length
这个关键字参数进去。-------------settings.py------------
# 将'UTC'改为'Asia/Shanghai'
TIME_ZONE = 'Asia/Shanghai'
# 将控制UTC时区的开关关闭,不关会报错
USE_TZ = False
---------models.py---------
create_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
其他字段类型 | 作用 |
---|---|
FileField | ⽤来存储⽂件的。 |
ImageField | ⽤来存储图⽚⽂件的。 |
FloatField | 浮点类型。映射到数据库中是float类型。 IntegerField |
BigIntegerField | ⼤整形。值的区间是-9223372036854775808—— 9223372036854775807。 PositiveIntegerField |
SmallIntegerField | ⼩整形。值的区间是-32768——32767。 PositiveSmallIntegerField |
TextField | ⼤量的⽂本类型。映射到数据库中是longtext类型。 UUIDField |
URLField | 类似于CharField,只不过只能⽤来存储url格式的字符串。并且默认的 max_length是200。 |
Field字段的常⽤参数
尽量不要使⽤这个参数,也就是保持默认值False
。因为Django在处理字符串相 关的Field的时候,即使这个Field的null=False,如果你没有给这个Field传递任 何值,那么Django也会使⽤⼀个空的字符串""来作为默认值存储进去。因此如果再使⽤null=True,Django会产⽣两种空值的情形(NULL或者空字符串)
。 如果想要在表单验证的时候允许这个字符串为空,那么建议使⽤blank=True
。 如果你的Field是BooleanField,那么对应的可空的字段则为 NullBooleanField。模型中Meta配置
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,
pub_date = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'book_model'
ordering = ['pub_date'] # 正序
ordering = ['-pub_date'] # 倒序
外键ForeignKey
----------------------models.py-------------------------
class Category(models.Model):
name = models.CharField(max_length=100)
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# author = models.ForeignKey("User",on_delete=models.CASCADE)
category = models.ForeignKey("Category",on_delete=models.CASCA DE)
### 自相关
class Comment(models.Model):
content = models.TextField()
origin_comment = models.ForeignKey('self',on_delete=models.CAS CADE,null=True)
# 或者 #
origin_comment = models.ForeignKey('Comment',on_delete=model s.CASCADE,null=True)
--------------------views.py--------------------------
from django.shortcuts import render
from django.http import HttpResponse
from .models import Article,Category
def index(request):
category = Category(name="news")
category.save()
article = Article(title='PHP',content='123')
# 等于对象,就可以获取外键值
article.category = category
article.save()
### 外键删除设置参考
# 级联删除
# category = models.ForeignKey('Category', on_delete=models.CASCADE, null=True)
# PROTECT
# category = models.ForeignKey('Category', on_delete=models.PROTECT)
# SET_NULL 前提是可以为null
category = models.ForeignKey('Category', on_delete=models.SET_NULL, null=True)
# SET_DEFAULT 前提是需要把它改为别的外键的id
# category = models.ForeignKey('Category', on_delete=models.SET_DEFAULT, default=4)
# category = models.ForeignKey('Category', on_delete=models.SET_DEFAULT, default=Category.objects.get(pk=2))
# SET
# category = models.ForeignKey('Category', on_delete=models.SET(Category.objects.get(pk=2)))
------------------------views.py---------------
from django.shortcuts import render
from django.db import connection
from .models import Book
from django.http import HttpResponse
def add_book(request):
book = Book(name="Python",author="JR",price=78)
book.save()
return HttpResponse("书籍添加成功")
## 先查出来再删
book = Book.objects.get(pk=1)
book.delete()
# 先查出来再改
book = Book.objects.get(pk=2)
book.price = 200
book.save()
-------------------views.
1.根据主键进⾏查找
book = Book.objects.get(pk=1)
print(book)
2.根据字段来查找
book = Book.objects.filter(name="Python")
print(book)
3.查询所有
book = Book.objects.all()
article = Article.objects.get(id__exact=14)
article = Article.objects.get(id__exact=None)
## 可以得到Django执⾏的SQL语句。但是只能作⽤于QuerySet对 象上
print(article.query)
## select * from article where id=14;
## select * from article where id IS NULL;
article = Article.objects.filter(title__iexact='hello world')
## select * from article where title like 'hello world';
articles = Article.objects.filter(title__contains='hello')
## select * where title like binary '%hello%'
## binary指判断大小写
articles = Article.objects.filter(id__in=[1,2,3])
## select *from articles where id in (1,2,3)
## 当然也可以传递⼀个QuerySet对象进去。
# 查找标题为hello的⽂章分类,因为有外键引用,可以直接根据外键选出合适的
articles = Article.objects.filter(title__icontains="hello")
category = Category.objects.filter(article__in=articles)
# 查找⽂章ID为1,2,3的⽂章分类 合并上面的查询
category = Category.objects.filter(article__id__in=[1,2,3])
# 想要获取⽂章标题中包含"hello"的所有的分类
categories = Category.object.filter(article__title__contains="hell o")
# 将所有id⼤于4的⽂章全部都找出来。
articles = Article.objects.filter(id__gt=4)
## select * from articles where id > 4;
#查询时间范围
start_date = datetime(year=2019, month=12, day=1,hour=10,minute=0, second=0) 2 end_date = d
end_date = datetime(year=2019, month=12, day=30,hour=10,minute=0,s econd=0)
date_range = Common.objects.filter(test_date__range=(start_date,en d_date))
date_test = Common.objects.filter(test_date__date=datetime(year=20 18,month=12,day=19))
articles = Article.objects.filter(pub_date__year=2018)
articles = Article.objects.filter(pub_date__year__gte=2017)
# select ... where pub_date between '2018-01-01' and '2018-12-31';
# select ... where pub_date >= '2017-01-01';
articles = Article.objects.filter(pub_date__time=time(hour=15,minu te=21,second=10))
1.Avg:求平均值。⽐如想要获取所有图书的价格平均值。那么可以使⽤以下代 码实现。
result = Book.objects.aggregate(Avg('price'))
print(result)
## {"price__avg":23.0}
## 其中price__avg的结构是根据field__avg规则构成的。如果想要修改默认的名字,那么 可以将Avg赋值给⼀个关键字参数
## result = Book.objects.aggregate(my_avg=Avg('price'))
## {"my_avg":23}
2.Count:获取指定的对象的个数
Count类中,还有另外⼀个参数叫做distinct,默认是等于False,如果是等于True,那 么将去掉那些重复的值
## ⽐如要获取作者表中所有的不重复的邮箱总共有多少个。
result = Author.objects.aggregate(count=Count('email',distinct=Tr ue))
# 统计每本图书的销量
result = Book.objects.annotate(book_nums=Count("bookorder"))
for book in result:
print("%s/%s"%(book.name,book.book_nums))
3.Max和Min:获取指定对象的最⼤值和最⼩值。⽐如想要获取Author表中,最 ⼤的年龄和最⼩的年龄分别是多少。
result = Author.objects.aggregate(Max('age'),Min('age'))
## {"age__max":88,"age__min":18}
# 统计每本售卖图书的最⼤值和最⼩值
request = Book.objects.annotate(max=Max("bookorder__price"),min=Mi n("bookorder__price"))
4.Sum:求指定对象的总和。⽐如要求图书的销售总额。
# 每⼀本图书的销售总额
result = Book.objects.annotate(total=Sum("bookorder__price"))
# 统计2019年,销售总额
result = BookOrder.objects.filter(create_time__year=2019).aggregat e(total=Sum("price"))
F表达式
----------普通方法-----------
employees = Employee.objects.all()
for employee in employees:
employee.salary += 1000
employee.save()
-----------F表达式-----------
from djang.db.models import F
Employee.objects.update(salary=F("salary")+1000)
# F表达式并不会⻢上从数据库中获取数据,⽽是在⽣成SQL语句的时候,动态的获取传给F表达 式的值。
## 如果想要获取作者中,name和email相同的作者数据。如果不使⽤F表达 式。
----------普通方法-----------
authors = Author.objects.all()
for author in authors:
if author.name == author.email:
print(author)
-----------F表达式-----------
from django.db.models import F
authors = Author.objects.filter(name=F("email"))
Q表达式
# 获取id等于3,或者名字中包含⽂字"传"的图书
5 books = Book.objects.filter(Q(id=3)|Q(name__contains="传"))
# 获取书名包含"传",但是id不等于3的图书
books = Book.objects.filter(Q(name__contains='传') & ~Q(id=3))
QuerySet API
QuerySet的⽅法
# 提取那些标题不包含`hello`的图书
Article.objects.exclude(title__contains='hello')
# 将在每个对象中都添加⼀个`author__name`的字段,⽤来显示这个⽂章的作者的年龄
articles = Article.objects.annotate(author_name=F("author__name"))
排序
。如果要倒叙排序, 那么可以在这个字段的前⾯加⼀个负号
# 根据创建的时间正序排序(从⼩到⼤,默认排序规则)
articles = Article.objects.order_by("create_time")
# 根据创建的时间倒序排序
articles = Article.objects.order_by("-create_time")
# 根据作者的名字进⾏排序
articles = Article.objects.order_by("author__name")
# ⾸先根据创建的时间进⾏排序,如果时间相同,则根据作者的名字进⾏排序
articles = Article.objects.order_by("create_time",'author__name' )
# 根据图书订单的评分来排序
articles = BookOrder.objects.order_by("book__rating")
指定取哪个字段
。返回的是字典,和以前不一样,不能和以前一样取数据,把所有字段都取出来了,取所有字段,可以指定字段来取,大大提高数据库的性能需要的时候查哪些 articles = Article.objects.values("title",'content')
for article in articles:
print(article)
articles = Article.objects.values_list("id","title")
print(articles)
# 等
⼀对多
或者 ⼀对⼀
中,不能⽤在 多对多 或者 多对⼀ 中。⽐如可以提前获取⽂章的作者,但是不能通过作者获取这个作者的 ⽂章,或者是通过某篇⽂章获取这个⽂章所有的标签-----普通的查询-------
article = Article.objects.get(pk=1)
article.author # 重新执⾏⼀次查询语句
---------select_related--------
article = Article.objects.select_related("author").get(pk=2)
article.author # 不需要重新执⾏查询语句了
多对⼀
和 多对多
的关系的查询问题。⽐如要获取标题中带有 hello 字符串的⽂章以及 他的所有标签 from django.db import connection
articles = Article.objects.prefetch_related("tag_set").filter(titl e__contains='hello')
print(articles.query) # 通过这条命令查看在底层的SQL语句
for article in articles:
print("title:",article.title)
print(article.tag_set.all())
article = Article(title='abc')
article.save()
# 下⾯这⾏代码相当于以上两⾏代码
article = Article.objects.create(title='abc')
obj,created= Category.objects.get_or_create(title='默认分类')
obj,created= Category.objects.get_or_create(title='默认分类')
# 如果有标题等于 默认分类 的分类,那么就会查找出来,如果没有,则会创建并 且存储到数据库中。
# 这个⽅法的返回值是⼀个元组,元组的第⼀个参数 obj 是这个对象,第⼆个参 数 created 代表是否创建的。
if Article.objects.filter(title__contains='hello').exists():
print(True)
# ⽐使⽤count更⾼效:
if Article.objects.filter(title__contains='hello').count() > 0:
# 也⽐直接判断QuerySet更⾼效
if Article.objects.filter(title__contains='hello'):
Article.objects.filter(category__isnull=True).update(category_id=3 )
Article.objects.filter(category__isnull=True).update(category_id=3 )
Article.objects.filter(category__isnull=True).update(category_id=3 )
books = Book.objects.all()[1:3]
for book in books:
print(book)
将QuerySet转换为SQL去执⾏
from django.db import connection
books = Book.objects.all()
print(connection.queries) # 空的列表。说明上⾯的QuerySet并没有真正的执⾏。
迭代
:在遍历QuerySet对象的时候,会⾸先先执⾏这个SQL语句,然后再把 这个结果返回进⾏迭代。⽐如以下代码就会转换为SQL语句:for book in Book.objects.all():
print(book)
使⽤步⻓做切⽚操作
:QuerySet可以类似于列表⼀样做切⽚操作。做切⽚操 作本身不会执⾏SQL语句,但是如果如果在做切⽚操作的时候提供了步⻓,那么就会⽴⻢执⾏SQL语句。需要注意的是,做切⽚后不能再执⾏filter⽅ 法,否则会报错。调⽤len函数
:调⽤len函数⽤来获取QuerySet中总共有多少条数据也会执⾏ SQL语句。调⽤list函数
:调⽤list函数⽤来将⼀个QuerySet对象转换为list对象也会⽴⻢ 执⾏SQL语句。判断
:如果对某个QuerySet进⾏判断,也会⽴⻢执⾏SQL语句。迁移命令
⽣成迁移脚本
。模型所在的app,必须放在 settings.py中的INSTALLED_APPS中。这个命令有以下⼏个常⽤选项:
app_label
:后⾯可以跟⼀个或者多个app,那么就只会针对这⼏个app⽣成 迁移脚本。 如果没有任何的app_label,那么会检查INSTALLED_APPS中所有的app下 的模型,针对每⼀个app都⽣成响应的迁移脚本。--name
:给这个迁移脚本指定⼀个名字。--empty
:⽣成⼀个空的迁移脚本。一般用不到。如果你想写⾃⼰的迁移脚本,可以使⽤ 这个命令来实现⼀个空的⽂件, 然后⾃⼰再在⽂件中写迁移脚本。映射到数据库
中。创建新的表或者修改表的 结构。以下⼀些常⽤的选项:
app_label
:将某个app下的迁移脚本映射到数据库中。如果没有指定,那么 会将所有在INSTALLED_APPS中的app下的模型都映射到数据库中。app_label migrationname
:给将某个app下指定名字的migration⽂件映射到 数据库中。--fake
:可以将指定的迁移脚本名字添加到数据库中。但是并不会把迁移脚 本转换为SQL语句,修改数据库中的表。--fake-initial
:将第⼀次⽣成的迁移⽂件版本号记录在数据库中。但并不会 真正的执⾏迁移脚本。python manage.py showmigrations [app名字]
python manage.py sqlmigrate book 0001_initial
migrate在这个过程中做了啥
将Django的核⼼表映射到数据库中
根据已有的表⾃动⽣成模型
inspectdb
的命令,可以⾮常⽅便的将已经存在的 表,⾃动的⽣成模型。想要使⽤inspectdb⾃动将表⽣成模型。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': "migrations_demo",
'HOST': '127.0.0.1',
'PORT': '3306',
'USER': 'root',
'PASSWORD': 'root'
}
}
python manage.py inspectdb > models.py
managed=False必须删掉
,如果保留这 个,那么以后这个模型有任何的修改,使⽤migrate都不会映射到数据库中。class Article(models.Model):
title = models.CharField(max_length=100, blank=True, null=True )
content = models.TextField(blank=True, null=True)
author = models.ForeignKey('front.User', models.SET_NULL, blan k=True, null=True)
tags = models.ManyToManyField("Tag",db_table='article_tag')
class Meta:
db_table = 'article
常⽤的请求
设置请求方式
from django.views.decorators.http import require_http_methods,require_GET,require_POST,require_safe
# require_GET是require_http_methods(['GET'])的简写形式,里面封装了这个方法
# @require_http_methods(['GET'])
@require_GET
def get_func(request):
return HttpResponse('get_func')
# @require_http_methods(['POST'])
@require_POST
def post_func(request):
return HttpResponse('post_func')
# 这个装饰器相当于是require_http_methods(['GET','HEAD'])的简写形式,
# 只允许使⽤相对安全的⽅式来访问视图。因为GET和HEAD不会对服务器产⽣增删改的⾏为
@require_safe
def my_view(request):
pass
限制请求装饰器
重定向
设置重定向
from django.shortcuts import reverse,redirect
def profile(request):
if request.GET.get("username"):
return HttpResponse("%s,欢迎来到个⼈中⼼⻚⾯!")
else:
return redirect(reverse("user:login"))
WSGIRequest对象
WSGIRequest对象常⽤属性
包含了所有上传的⽂件
。所有 header 信息
。
WSGIRequest对象常⽤⽅法
--------------views.py---------------
def index(request):
# a = 1/0
from django.core.handlers.wsgi import WSGIRequest
# print(type(request))
# print(request.path)
# print(request.method)
# print(request.GET.get('page'))
# print(request.POST.get('page'))
# print(request.COOKIES)
# print(request.META)
# for key, value in request.META.items():
# print(key, value)
# print(request.is_secure())
# get_host():服务器的域名。如果在访问的时候还有端口号,那么会加上端口号。比如www.baidu.com: 9000。
print(request.get_host())
# get_full_path():返回完整的path。如果有查询字符串,还会加上查询字符串。比如 / music / bands /?print = True。
print(request.get_full_path())
# get_raw_uri():获取请求的完整url。
print(request.get_raw_uri())
return HttpResponse('hello world')
常用返回对象
HttpResponse对象
--------------views.py---------------
response = HttpResponse()
response.content = "⾸⻚"
return response
--------------views.py---------------
def book(request):
# return HttpResponse('喝高了
', content_type='text/plain;charset=utf-8')
res = HttpResponse()
# res.content = 'book'
# res.status_code = 404
# res.content = 'hello
'
res['username'] = 'cheney'
res.write('hello')
return res
JsonResponse对象
--------------views.py---------------
from django.http import JsonResponse
def index(request):
return JsonResponse({"username":"juran","age":18}
from django.http import JsonResponse
def index(request):
persons = ['张三','李四','王五']
# return JsonResponse(persons) # 会报错
return JsonResponse(persons,safe=False,json_dumps_params={'ensure_ ascii':False})
类视图
View
所有的类视图都是继承⾃他
。如果我们写⾃⼰的类视图,也可以继承⾃他。然后再根据当前请求的 method,来实现不同的⽅法。⽐如这个视图只能使⽤get的⽅式来请求,那么 就可以在这个类中定义get(self,request,args,kwargs)⽅法。以此类推,如果 只需要实现post⽅法,那么就只需要在类中实现 post(self,request,args,kwargs)。--------------views.py---------------
from django.views import View
class BookDetailView(View):
def get(self,request,*args,**kwargs):
return render(request,'detail.html')
-------------urls.py--------------------
# 类视图写完后,还应该在urls.py中进⾏映射
# 映射的时候就需要调⽤View的类⽅法as_view()来进⾏转换。⾃动查找指定⽅法。
urlpatterns = [
path("detail//",
views.BookDetailView.as_view(),name= 'detail')
]
--------------views.py---------------
# 请求方法错误时,比如这里只定了post方法,如果我们get请求的话就会调用http_method_not_allowed
class AddBookView(View):
def post(self,request,*args,**kwargs):
return HttpResponse("书籍添加成功!")
def http_method_not_allowed(self, request, *args, **kwargs):
return HttpResponse("您当前采⽤的method是:%s,本视图只⽀持使⽤po st请求!" % request.method)
-------------urls.py--------------------
path("addbook/",views.AddBookView.as_view(),name='add_book')
TemplateView
# 模版中需要传递get_context_data参数
--------------views.py---------------
from django.views.generic.base import TemplateView
class HomePageView(TemplateView):
template_name = "home.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['username'] = "juran"
return context
-------------urls.py--------------------
from django.urls import path
from .views import HomePageView
urlpatterns = [
path('', HomePageView.as_view(), name='home'),
path('about/', TemplateView.as_view(template_name="about.html"),
]
# 模版中不需要传递参数,直接只在urls.py中使⽤ TemplateView来渲染模版。
-------------urls.py--------------------
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path('about/', TemplateView.as_view(template_name="about.html"),
]
ListView
--------------------article_list.html----------------
Title
{% for article in articles %}
- {{ article.title }}-{{ article.content }}
{% endfor %}
--------------------models.py----------------
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=20)
content = models.TextField()
create_time = models.DateTimeField(auto_now_add=True)
--------------------urls.py--------------------
from django.urls import path
from . import views
urlpatterns = [
path('', views.ArticleListView.as_view(), name='list'),
]
--------------------views.py----------------
from .models import Article
from django.views.generic import ListView
class ArticleListView(ListView):
# 数据库模型
model = Article
# 需要分页的模板
template_name = 'article_list.html'
# 每页的数据量
paginate_by = 10
# 查询出来的数据的变量的名字
context_object_name = 'articles'
# 通过create_time 这个字段来进行排序
ordering = 'create_time'
# 进行翻页的参数
page_kwarg = 'page'
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
# print(context)
# for key, value in context.items():
# print(key, value)
# paginator = context.get('paginator')
# # count:总共有多少条数据。
# print(paginator.count)
# # num_pages:总共有多少页。
# print(paginator.num_pages)
# # page_range:页面的区间。比如有三页,那么就range(1, 4)。
# print(paginator.page_range)
page = context.get('page_obj')
# has_next:是否还有下一页。
print(page.has_next())
# has_previous:是否还有上一页。
print(page.has_previous())
# next_page_number:下一页的页码。
# previous_page_number:上一页的页码。
# number:当前页。
print(page.number)
# start_index:当前这一页的第一条数据的索引值。
# end_index:当前这一页的最后一条数据的索引值。
return context
def get_queryset(self):
return Article.objects.filter(id__lte=89)
错误处理
常⽤的错误码
⾃定义错误模板
--------------------settings.py---------------- DEBUG = False ALLOWED_HOSTS = ["127.0.0.1"]
--------------------templates/405.html----------------
Title
405错误
# templates⽂件夹下,可重定向到错误页面
---------------views.py--------------
def index(request):
return redirect(reverse('405'))
# 或者专⻔定义⼀个app,⽤来处理这些错误
---------------urls.py--------------
from django.urls import path
from . import views
urlpatterns = [
path("405",views.view_405,name="405")
]
---------------views.py--------------
from django.http import HttpResponse
from django.shortcuts import render
def view_405(request):
return render(request,"errors/405.html",status=405)
HTML中的表单:
Django中的表单
Django中表单使⽤流程
----------------forms.py----------------
class MessageBoardForm(forms.Form):
title = forms.CharField(max_length=3,label='标题',min_length=2, error_messages={"min_length":'标题字符段不符合要求!'})
content = forms.CharField(widget=forms.Textarea,label='内容',er ror_messages={"required":'content字段必须填写!'})
email = forms.EmailField(label='邮箱')
reply = forms.BooleanField(required=False,label='回复')
----------------views.py----------------
from .forms import MessageForm
from django.views import View
from django.forms.utils import ErrorDict
class IndexView(View):
# 在使⽤GET请求的时候,我们传了⼀个form给模板,那么以后模板就可以使⽤ form来⽣成⼀个表单的html代码
def get(self,request):
form = MessageBoardForm()
return render(request,'index.html',{'form':form})
# 在使⽤POST请求的时候,我们根据前端上传上来的数据,构建⼀个新的表单
# 这个表单是⽤来验证数据是否合法的,如果数据都验证通过了,那么我们可以通过cleaned_data来获取相应的数据。在模板中渲染表单的HTML
def post(self,request):
form = MessageBoardForm(request.POST)
if form.is_valid():
title = form.cleaned_data.get('title')
content = form.cleaned_data.get('content')
email = form.cleaned_data.get('email')
reply = form.cleaned_data.get('reply')
return HttpResponse('success')
else:
print(form.errors)
return HttpResponse('fail')
-----------urls.py--------------------
from django.urls import path
from . import views
urlpatterns = [
path('', views.IndexView.as_view(), name='index')
]
# 我们在最外⾯给了⼀个form标签,然后在⾥⾯使⽤了table标签来进⾏美化,在 使⽤form对象渲染的时候,使⽤的是table的⽅式,当然还可以使⽤ul的⽅式 (as_ul),也可以使⽤p标签的⽅式(as_p),并且在后⾯我们还加上了⼀个 提交按钮。这样就可以⽣成⼀个表单了
----------------index.html---------------
常⽤的Field
常⽤验证器
------------------forms.py-----------------
class MyForm(forms.Form):
telephone = forms.CharField(validators=[validators.RegexValida tor("1[345678]\d{9}",message='请输⼊正确格式的⼿机号码!')])
⾃定义验证
# # 验证数据对某个字段进⾏验证
------------------forms.py-----------------
class MyForm(forms.Form):
telephone = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}",message='请输⼊正确格式的⼿机号码!')])
def clean_telephone(self):
telephone = self.cleaned_data.get('telephone')
exists = User.objects.filter(telephone=telephone).exists()
if exists:
raise forms.ValidationError("⼿机号码已经存在!")
return telephone
------------------forms.py-----------------
# 针对多个字段进⾏验证,那么可以重写clean⽅法。⽐如要在注册的时候,要判断提交的两个密码是否相等
class MyForm(forms.Form):
telephone = forms.CharField(validators=[validators.RegexValid ator("1[345678]\d{9}",message='请输⼊正确格式的⼿机号码!')])
pwd1 = forms.CharField(max_length=12)
pwd2 = forms.CharField(max_length=12)
def clean(self):
cleaned_data = super().clean()
pwd1 = cleaned_data.get('pwd1')
pwd2 = cleaned_data.get('pwd2')
if pwd1 != pwd2:
raise forms.ValidationError('两个密码不⼀致!')
提取错误信息
提取错误处理
# 这样就可以把某个字段所有的错误信息直接放在这个列表中
class MyForm(forms.Form):
username = forms.URLField(max_length=4)
def get_errors(self):
errors = self.errors.get_json_data()
new_errors = {}
for key,message_dicts in errors.items():
messages = []
for message in message_dicts:
messages.append(message['message'])
new_errors[key] = messages
return new_errors
ModelForm
# 现在有个Article的模型。
------------------models.py-----------------
from django.db import models
from django.core import validators
class Article(models.Model):
title = models.CharField(max_length=10,validators=[validators. MinLengthValidator(limit_value=3)])
content = models.TextField()
author = models.CharField(max_length=100)
category = models.CharField(max_length=100)
create_time = models.DateTimeField(auto_now_add=True)
# 那么在写表单的时候,就不需要把Article模型中所有的字段都⼀个个重复写⼀ 遍了。
------------------forms.py-----------------
from django import forms
# MyForm是继承⾃forms.ModelForm,然后在表单中定义了⼀个Meta类
# 在 Meta类中指定了model=Article,以及fields="all",这样就可以将Article模型 中所有的字段都复制过来,进⾏验证。
class MyForm(forms.ModelForm):
class Meta:
model = Article
fields = "__all__"
fields="all"
这样就可以将Article模型 中所有的字段都复制过来,进⾏验证。fields = ['title','content']
exclude = ['category']
class MyForm(forms.ModelForm):
class Meta:
model = Article 4 exclude = ['category']
error_messages ={
'title':{
'max_length': '最多不能超过10个字符!',
'min_length': '最少不能少于3个字符!'
},
'content': {
'required': '必须输⼊content!',
}
}
form = MyForm(request.POST)
if form.is_valid():
form.save()
return HttpResponse('succes')
else:
print(form.get_errors())
return HttpResponse('fail')
# 如果表单上验证的字段没有包含模型中所有的字段,这时候就可以先创建对象,
# 再根据填充其他字段,把所有字段的值都补充完成后,再保存到数据库中。
form = MyForm(request.POST)
if form.is_valid():
article = form.save(commit=False)
article.category = 'Python'
article.save()
return HttpResponse('succes')
else:
print(form.get_errors())
return HttpResponse('fail')
前端HTML代码实现
---------------upload.html--------------
Title
后端的代码实现
---------------- models.py-------------------
from django.db import models
from django.core import validators
class Article(models.Model):
title = models.CharField(max_length=20, validators=[validators.MinLengthValidator(limit_value=3)], error_messages={'min_length': '最小长度为3'})
content = models.TextField()
author = models.CharField(max_length=20)
create_time = models.DateTimeField(auto_now_add=True)
images = models.FileField(upload_to="files",null=True)
---------------- urls.py-------------------
from django.urls import path
from . import views
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('upload/', views.UploadView.as_view(), name='upload'),
]
--------------forms.py------------------
class UploadForms(forms.ModelForm):
class Meta:
model = Article
fields = '__all__'
error_messages = {
'images': {
}
}
--------------Views.py------------------
from django.shortcuts import render
from django.views import View
from .forms import UploadForms
from django.http import HttpResponse
from .models import Article
class UploadView(View):
def get(self, request):
return render(request, 'upload.html')
def post(self, request):
# image = request.FILES.get('images')
# print(request.POST)
# print(image)
# print(type(image))
# with open('demo.png', 'wb') as f:
# f.write(image.read())
form = UploadForms(request.POST, request.FILES)
print(form)
if form.is_valid():
title = request.POST.get('title')
content = request.POST.get('content')
author = request.POST.get('author')
# 接收文件
# 通过request.FILES接收到⽂件后
images = request.FILES.get('images')
article = Article(title=title, content=content, author=author, images=images)
# 再写⼊到指定的地⽅。这样就可以 完成⼀个⽂件的上传功能了。
# 调⽤完article.save()⽅法,就会把⽂件保存到files下⾯,并且会将这个⽂件的 路径存储到数据库中。
article.save()
return HttpResponse('success')
else:
print(form.errors.get_json_data())
return HttpResponse('fail')
-----------------settings.py-----配置文件夹----
## 指定MEDIA_ROOT⽬录。
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
-------------urls.py.py------添加MEDIA_ROOT⽬录下的访问路径
urlpatterns = [
path('', views.index),
] + static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
-----------------models.py---------------
# 如果我们同时指定MEDIA_ROOT和upload_to,那么会将⽂件上传到 MEDIA_ROOT下的upload_to⽂件夹中。
#小文件夹命名方式: '%Y/%m/%d/' 增加文件 年月日,这样方便分类管理
images = models.FileField(upload_to='%Y/%m/%d/', null=True)
限制上传的⽂件拓展名
# 普通的Form表单
----------------models.py----------------
thumbnial=models.FileField(upload_to='%Y/%m/%d/',validators =[validators.FileExtensionValidator(['txt','pdf'])])
----------------forms.py----------------
class BookForms(forms.Form): 10 files = forms.FileField(validators=[validators.FileExtensionV alidator(['txt'],message="必须是TXT")])
---------------views.py-----------------
class uploadfile(View):
def get(self,request):
return render(request,"upload.html")
def post(self,request):
form = BookForms(request.POST,request.FILES)
if form.is_valid():
title = request.POST.get('title')
files = request.FILES.get('files')
FilesUpload.objects.create(title=title,files=files)
return HttpResponse("success")
else:
print(form.errors.get_json_data())
return HttpResponse("fail")
上传图⽚
----------------models.py-----------
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
thumbnail = models.ImageField(upload_to="%Y/%m/%d/")
--------------forms.py------------
class BookForms(forms.Form):
files = forms.ImageField(error_messages={"invalid_image":"格式 不对"})
cookie和session介绍
1.cookie:在⽹站中,http请求是⽆状态的。也就是说即使第⼀次和服务器连接 后并且登录成功后,第⼆次请求服务器依然不能知道当前请求是哪个⽤户。 cookie的出现就是为了解决这个问题,第⼀次登录后服务器返回⼀些数据 (cookie)给浏览器,然后浏览器保存在本地,当该⽤户发送第⼆次请求的时 候,就会⾃动的把上次请求存储的cookie数据⾃动的携带给服务器,服务器通 过浏览器携带的数据就能判断当前⽤户是哪个了。cookie存储的数据量有限, 不同的浏览器有不同的存储⼤⼩,但⼀般不超过4KB。因此使⽤cookie只能存 储⼀些⼩量的数据
2.session: session和cookie的作⽤有点类似,都是为了存储⽤户相关的信息。 不同的是,cookie是存储在本地浏览器,session是⼀个思路、⼀个概念、⼀个 服务器存储授权信息的解决⽅案,不同的服务器,不同的框架,不同的语⾔有 不同的实现。虽然实现不⼀样,但是他们的⽬的都是服务器为了⽅便存储数据 的。session的出现,是为了解决cookie存储数据不安全的问题的。
3.cookie和session使⽤:web开发发展⾄今,cookie和session的使⽤已经出 现了⼀些⾮常成熟的⽅案。在如今的市场或者企业⾥,⼀般有两种存储⽅式:
参考文档:session和cookie:https://www.cnblogs.com/sss4/p/7071334.html
from datetime import datetime
from django.utils.timezone import make_aware
def cookie_test(request):
response = HttpResponse("index")
expires = make_aware(datetime(year=2018,month=12,day=27,hour= 3,minute=20,second=0))
response.set_cookie("username","juran",expires=expires,path= "/cms/")
return response
def get_cookie_test(request):
cookies = request.COOKIES 13 username = cookies.get('username')
return HttpResponse(username)
def delete_cookie(request):
response = HttpResponse('delete')
response.delete_cookie('username')
return response
cookies = request.COOKIES
for cookie_key,cookie_value in cookies.items():
print(cookie_key,cookie_value)
def index(request):
request.session['username'] = 'jr'
request.session.get('username')
return HttpResponse('index')
----------------settings.py-----------------
CACHES = {
'default':{
'BACKEND':'django.core.cache.backends.memcached.MemcachedC ache',
'LOCATION':'127.0.0.1:11211'
}
}
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
上下⽂处理器介绍
def frontuser(request):
userid = request.session.get("userid")
userModel = models.FrontendUser.objects.filter(pk=userid).first ()
if userModel:
return {'frontuser':userModel}
else:
return {}
with Path(CURRENT_DIR, 'templates', 'technical_500.html').open() as fh:
修改成:
with Path(CURRENT_DIR, 'templates', 'technical_500.html').open(encoding='utf-8') as fh:
migrations中的迁移版本和数据库中的迁移版本对不上怎么办?
前面删掉数据库一部分的迁移记录,迁移的时候报错