# 不指定版本,默认安装最新版本
pip3 install django
# 指定安装版本
pip3 install django==1.11.11
# 版本更换(已有其他版本时无需卸载,直接安装会自动卸载安装新版)
pip3 install django==1.11.18
# 命令行验证
django-admin check
1. File -> Settings -> Project:xxx -> Project Interpreter
2. 点击"+" 添加 -> 搜索 "django"
3. 右侧勾选 □Specify version,选中需要安装的版本
4. 点击Install Package
# 创建项目
django-admin startproject xxxxx
例如:django-admin startproject mysite
# 启动django项目(先切入到项目目录下,再启动)
cd ~/mysite
python manage.py runserver
# (默认8000端口)
# 指定端口启动项目
python manage.py runserver 127.0.0.1:6666
# 命令行创建应用
python manage.py startapp xxxxx
例如:
python manage.py startapp login
python manage.py startapp gateway
1. File -> New Project -> Django
2. Location: D:\django\xxxxx (xxxxx定义一个项目名)
3. More Settings -> Application name [xxxxx] (xxxxx定义项目下一个应用模块名)
# 下方Terminal中可以操作命令行的命令(同命令行常规操作)
# 项目目录:
settings.py # 配置文件
urls.py # 路由与视图函数对应关系(连接浏览器用户地址与后台对应功能类、函数方法等)
wsgi.py # wsgiref模块(框架封装了socket代码功能)
manage.py # django的入口文件
db.sqlite3 # django自带的sqlite3数据库,一般采用MySQL替代
templates # 模版文件夹(存储一些html页面文件)
# 应用模块目录,例如python manage.py startapp login,则login目录下有
admin.py # django后台管理
apps.py # 注册使用
migrations # 文件夹(数据库迁移记录文件存放)
models.py # 数据库相关的 模型类(orm)
tests.py # 测试文件
views.py # 视图函数(视图层,功能,业务逻辑的视图函数)
每次使用python manage.py startapp xxxxx创建各类应用时,需要去settings.py中的INSTALLED_APPS注册服务,否则项目下创建的应用无法生效
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'app02',
'app03',
'app04.apps.App04Config'
]
# 可以直接缩写例如'login','metadata'
# 也可以按照'index.apps.App01Config','register.apps.App04Config'格式
from django.shortcuts import render, HttpResponse, redirect
# return HttpResponse('hello') 相当于浏览器直接看到hello字符内容
# return render(request, 'login.html') 相当于把一个login.html页面展示给浏览器
# return redirect('/userlist/') 相当于把当前的请求最终返回时重定向跳转到127.0.0.1:8000/userlist/
"""
视图函数render, HttpResponse, redirect本质其实就是最终必须返回一个HttpResponse对象
"""
# 查看render源码
def render(request, template_name, context=None, content_type=None, status=None, using=None):
content = loader.render_to_string(template_name, context, request, using=using)
return HttpResponse(content, content_type, status)
# 查看redirect源码
def redirect(to, *args, **kwargs):
if kwargs.pop('permanent', False):
redirect_class = HttpResponsePermanentRedirect
else:
redirect_class = HttpResponseRedirect
return redirect_class(resolve_url(to, *args, **kwargs))
"""
redirect_class = HttpResponseRedirect
class HttpResponseRedirect(HttpResponseRedirectBase):
class HttpResponseRedirectBase(HttpResponse):
class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
class HttpResponseRedirectBase(HttpResponse):
最终还是继承了HttpResponse
"""
# 在view.py写后端业务逻辑时,locals()会将所在的名称空间中所有的值传给html前端
# 示例:
# 后端
def ab_render(request):
userinfo = models.User.objects.all()
return render(request,'userinfo.html',locals())
# 前端
{% for user_obj in userinfo %}
<tr>
<td>{{ user_obj.id }}</td>
<td>{{ user_obj.username }}</td>
<td>{{ user_obj.password }}</td>
</tr>
{% endfor %}
request.method # 返回请求方式(POST/GET...) ,并且是全大写的字符串形式
request.POST # 获取用户post请求提交的普通数据不包含文件(数据没有大小限制)
request.POST.get() # 只获取列表最后一个元素
request.POST.getlist() # 直接将列表取出
request.GET # 获取用户提交的get请求数据(数据有大小限制),相当于获取url里"?"后面携带的参数
request.GET.get() # 只获取列表最后一个元素
request.GET.getlist() # 直接将列表取出
request.FILES # 文件处理
request.body # 原生浏览器发过来的二进制数据
request.path # 请求路径
request.get_full_path() # 能过获取完整的url包括get请求问号后面携带的参数
# settings.py中,数据库初始配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# 改成MySQL数据库连接
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'wangt',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '192.168.100.15',
'PORT': 3306,
'CHARSET': 'utf8'
}
}
#【注意】: 一般python开发涉及MySQL基本都是用pymysql模块,django默认用的是mysqldb模块链接MySQL,需要配置用pymysql链接,更改项目目录下__init__.py文件,添加:
import pymysql
pymysql.install_as_MySQLdb()
ORM: 对象关系映射
类比:
类[class] -> 表[table]
对象[object] -> 记录
对象属性/方法 -> 记录某个字段中对应的值,也就是数据
# 【注意】 orm不能创建database,需要在MySQL提前创建库
# models.py中,数据库相关的 模型类(orm)
class Userinfo(models.Model):
id = models.AutoField(primary_key=True,verbose_name='主键ID')
username = models.CharField(max_length=32,verbose_name='用户名')
password = models.IntegerField(verbose_name='登陆密码')
# CharField中必须要指定max_length=xx
# verbose_name相当于建表时的字段注释
# 常见操作还有null=True,default='123456'
# 只要在models.py中操作数据库,需要将记录同步至migrations目录中
python manage.py makemigrations #记录至文件
python manage.py migrate # 同步至MySQL
# 修改表结构对应代码更改后执行makemigrations和migrate
# 数据增加
# create()
user_obj = models.UserInfo.objects.create(**kwargs)
user_obj = models.User(**kwargs)
user_obj.save()
# 数据查询
# filter(),括号中加查询条件,类似sql中的where关键词
result = models.UserInfo.objects.filter(**kwargs)
# 查询全部数据
result = models.User.objects.filter()
result = models.User.objects.all()
# ORM中创建表与表的关系 (一对多、一对一、多对多)
models.ForeignKey(to='tablename')
models.ManyToManyField(to='tablename')
models.OneToOneField(to='tablename')
# 路由匹配
url(r'user',views.user),
url(r'userinfo',views.userinfo)
"""
url方法第一个参数是正则表达式
只要第一个参数正则表达式能够匹配到内容 那么就会立刻停止往下匹配,例如已经匹配上user,就会去执行user对应的视图函数函数,不会继续匹配到userinfo
在前端浏览器输入url地址的时候框架会默认尾部加斜杠,django内部做了重定向,一次匹配不到则url后面加斜杠再尝试匹配
"""
urlpatterns = [
# 首页(^$相当于什么也没有,等同于后面没有加/分页)
url(r'^$',views.home),
# ^user/$相当于匹配user开头且user结尾后面不跟其它内容
url(r'^user/$',views.user),
url(r'^userinfo/$',views.userinfo),
# 尾页(相当与上面挨个匹配都没匹配上,才会轮到匹配尾页这一项,通常用来配置404页面)
url(r'',views.error),
]
# 浏览器输入地址栏,后面自动帮拼接/结尾的功能是
settings.py中
APPEND_SLASH = True
# 默认就是True,所以文件中无此配置项,如果想关闭自动加斜杠可以加配置APPEND_SLASH = False
url(r'^test/(\d+)/',views.test)
def test(request,xxx):
return HttpResponse('test')
# 无名分组就是将括号内正则表达式匹配到的内容当作位置参数传递给后面的视图函数,浏览器中输入的例如test/666,这个666传给xxx,如果是123则123转给xxx
# 可以给正则表达式起一个别名
url(r'^test/(?P\d+)' ,views.testadd)
def testadd(request,hello):
return HttpResponse('test')
# 有名分组就是将括号内正则表达式匹配到的内容当作关键字参数传递给后面的视图函数,浏览器中输入的例如test/666,这个666会有一个别名为hello,然后传给视图函数中的hello
# 通过一些方法得到一个结果,该结果可以直接访问对应的url触发视图函数里的方法
# 先给路由与视图函数起一个别名
url(r'^login/',views.login,name='yyds')
# 反向解析
# 后端反向解析[ 通过reverse实现 ]
from django.shortcuts import render,HttpResponse,redirect,reverse
reverse('yyds')
# 前端反向解析出yyds
<a href="{% url 'yyds' %}">666</a>
# 无名分组反向解析
url(r'^index/(\d+)/',views.index,name='xxx')
# 前端
{% url 'xxx' 666 %}
# 后端 需要import
reverse('xxx', args=(1,))
"""
数字666一般情况下放的是数据的主键值 数据的编辑和删除
url(r'^edit/(\d+)/',views.edit,name='xxx')
def edit(request,edit_id):
reverse('xxx',args=(edit_id,))
{%for user_obj in userlist%}
编辑
{%endfor%}
"""
# 有名分组反向解析
url(r'^func/(?P\d+)/' ,views.func,name='xxx')
# 前端
<a href="{% url 'xxx' 666 %}">222</a>
# 后端
print(reverse('xxx',args=(111,)))
# urls.py中
from app01 import urls as app01_urls
from app02 import urls as app02_urls
# 将urls中变成总路由
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 应用模块
url(r'^app01/',include('app01.urls')),
url(r'^app02/',include('app02.urls'))
]
# 各应用模块中创建urls.py子路由文件
# app01子路由 urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^reg/',views.reg)
]
# app02子路由 urls.py
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r'^reg/',views.reg)
]
# FBV
# url(r'^index/',views.index)
# 前端index.html
def index(request):
return HttpResponse('OK')
# CBV
# url(r'^login/$',views.MyClass.as_view())
# 前端index.html
from django.views import View
class MyClass(View):
def get(self,request):
return HttpResponse('get请求')
def post(self,request):
return HttpResponse('post请求')
# 打开index的get请求,返回get;页面上如有提交POST请求则触发post
# CBV路由匹配写法跟FBV有点不一样但是其实本质是一样的,CBV只是在内部做了一个分发的步骤
# 源码
# views.MyClass.as_view()
def as_view(cls, **initkwargs):
...
def view(request, *args, **kwargs):
...
return self.dispatch(request, *args, **kwargs)
...
return view
# as_view ->return view -> view中return dispatch
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
# http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
# 最终handler匹配上http_method_names其中的一种请求,最后触发类中方法
前端页面中
{{ }}: 表示变量相关的语法格式
{% %}: 逻辑相关的语法格式
def index(request):
n = 123 # 前端页面中 {{ n }}
可以获取到n的值
def func():
print('我被执行了')
return '你的另一半在等你'
class MyClass(object):
def get_self(self):
return 'self'
@staticmethod
def get_func():
return 'func'
@classmethod
def get_class(cls):
return 'cls'
# 对象obj被展示到html页面上 就类似于执行了打印操作也会触发__str__方法
def __str__(self):
return '__str__'
obj = MyClass()
# 前端页面触发方式
# {{ obj }}
# {{ obj.get_self }}
# {{ obj.get_func }}
# {{ obj.get_class }}
# return render(request,'index.html',{}) # 一个个传
return render(request,'index.html',locals())
# 过滤器就类似于是模版语法内置的 内置方法
# 基本语法
{{数据|过滤器:参数}} # 参数是可选项
# 当后端传的值带有标签关键词时,默认当作字符串一起读取,如果需要关键词生效则需要前端页面safe转义
|safe
# 第二种方式,直接在后端处理
from django.utils.safestring import mark_safe
res = mark_safe('连同h1标签一同生效
') # 相当于会被识别成html标签语言,也不会识别成字符串
"""
这里的数据一般都是用变量替代
统计长度:{{ 数据|length }}
默认值:{{ 数据|default:'非真' }}
# (第一个参数布尔值是True就展示第一个参数的值,否则展示冒号后面的值)
文件大小:{{ 数据|filesizeformat }}
日期格式化:{{ 数据|date:'Y-m-d H:i:s' }}
切片操作(支持步长):{{ 数据|slice:'0:4:2' }}
切取字符(包含三个点):{{ 数据|truncatechars:9 }}
# 默认切完结尾有"..."
切取单词(不包含三个点 按照空格切):{{ 数据|truncatewords:9 }}
切取单词(不包含三个点 按照空格切):{{ 数据|truncatewords:9 }}
移除特定的字符:{{ 数据|cut:' ' }}
拼接操作:{{ 数据|join:'$' }}
拼接操作(加法):{{ 数据|add:10 }}
拼接操作(加法):{{ 数据|add:msg }}
转义:{{ 数据|safe }}
转义:{{ 数据|safe }}
转义:{{ 数据 }}
"""
# 举例: 有个user表,字段name、age、register_time
# 增
# 插入一条记录
res = models.User.objects.create(name='wangt',age=18,register_time='2022-12-31')
print(res)
# 字段引用实时时间
import datetime
ctime = datetime.datetime.now()
user_obj = models.User(name='wangt',age=18,register_time=ctime)
user_obj.save()
# 删
# 删除主键为2的一条记录
res = models.User.objects.filter(pk=2).delete()
print(res)
"""
pk会自动查找到当前表的主键字段 指代的就是当前表的主键字段
使用pk后 无需考虑当前表的主键字段名是什么
如uid为主键 则删除uid为2的记录
如pid为主键 则删除pid为2的记录
如sid为主键 则删除sid为2的记录
...
"""
# 方式2
user_obj = models.User.objects.filter(pk=1).first()
user_obj.delete()
# 修改
# 把主键为4的记录,name字段名称修改
models.User.objects.filter(pk=4).update(name='wangtnew')
# 查
# 如果数据不存在会报错
user_obj = models.User.objects.get(pk=4)
# 如果数据不存在不报错,推荐使用
user_obj = models.User.objects.filter(pk=6)
# 常用方法
"""
.all() 查询所有数据
.filter() 带有过滤条件的查询
.get() 直接拿数据对象 但是条件不存在直接报错
.first() 拿queryset里面第一个元素
.last()
.values() 可以指定获取的数据字段 select name,age from ... 列表套字典
.values_list() 列表套元祖
.distinct() 去重
.order_by() 默认升序,以在字段前面加一个负改为降序
.reverse() 反转的前提是 数据已经排过序了
.count() 统计当前数据的个数
.exclude() 排除在外
"""
# filter()中常用的__双下划线条件
"""
age__gt=10 大于10岁
age__lt=10 小于10岁
age__gte=10 大于等于10岁
age__lte=10 小于等于10岁
age__in=[18,28,38] 年龄是18,28,38的结果
age__range=[18,28] 年龄是大于等于18,小于等于28的结果
name__contains='王' 查出名字里有"王"的结果
name__contains='w' 查出名字里有"w"的结果,区分大小写
name__icontains='w' 查出名字里有"w"的结果,前面加i则不区分大小写
name__startswith='w' 查出名字里w开头的结果
name__endswith='g' 查出名字里g结尾的结果
register_time__month='1' 查出register_time字段为1月时间的数据
register_time__year='2022' 查出register_time字段为2022年时间的数据
"""
"""
AutoField
primary_key=True 主键字段
CharField varchar
verbose_name 字段的注释
max_length 长度
IntegerField int
BigIntegerField bigint
DecimalField
max_digits=8
decimal_places=2
EmailFiled varchar(254)
DateField date
DateTimeField datetime
auto_now:每次修改数据的时候都会自动更新当前时间
auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了
BooleanField(Field) - 布尔值类型
该字段传布尔值(False/True) 数据库里面存0/1
TextField(Field) - 文本类型
该字段可以用来存大段内容(文章、博客...) 没有字数限制
后面的bbs作业 文章字段用的就是TextField
FileField(Field) - 字符类型
upload_to = "/data"
给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中
"""