Python大神 - Django(基础知识)--构建项目的思路
一、Django的优势
Python的WEB框架有Django、Tornado、Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等诸多功能
二、DJango项目的创建
1)命令行
django-admin startproject sitename # 建项目 python manage.py runserver 0.0.0.0 # 启动服务 python manage.py startapp appname # 创建app python manage.py syncdb # 建表是的编码是utf8(后面的版本取消) python manage.py makemigrations # 连接库 python manage.py migrate # 建表 python manage.py createsuperuser # 创建admin用户
2)pycharm
三、Django的目录结构
1)同项目名称一样的目录,是做全局变量的:setting(配置文件,路径配置等),urls(路由系统),wsgi(socket) 2)app文件:app,models,views,admin,tests 3)templates(存放html文件) 4)manage(进入Django命令行模式) 以下是自定义:log(存放日志),media(存放视频,照片),static(存放js,css,静态图片..)
四、连接数据库
注:新建的数据库要指定utf8格式,否则在建表时(有中文输入)会出现乱码
步骤:setting配置数据库,连接数据库,建表
1、setting配置DATABASES
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'dbname', 'USER': 'root', 'PASSWORD': 'xxx', 'HOST': '', 'PORT': '', } }
2、由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替
1 # 如下设置放置的与project同名的配置的 __init__.py文件中 2 import pymysql 3 pymysql.install_as_MySQLdb()
3、在setting配置模版文件路径(把html存放在templates)--默认已配置好
TEMPLATE_DIRS = (
os.path.join(BASE_DIR,'templates'),
)
4、在setting配置静态文件路径(把css,js存放在statics)
注:在生产环境下(DEBUG=False),该配置失效
STATICFILES_DIRS = ( os.path.join(BASE_DIR,'static'), ) 注:在html里应用静态文件,建议使用模板语言 1)在html开头添加 {% load staticfiles %} 2)应用静态文件 {% static 'css/reset.css' %}
5、当涉及到文件上传时,在setting配置
注:涉及到文件上传的upload # 配置上传文件的路径 ========================================= MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR,"media") =========================================
2)url配置
==================================================================
旧版本:
from django.views.static import serve # 前端显示出图片内容,旧版本
from TFF.settings import MEDIA_ROOT
# url(r'^madia/(?P<path>.*)$', serve,{"document_root":MEDIA_ROOT}), # 旧版本
==================================================================
# 新版本
from django.conf.urls.static import static
from TFF import settings
urlpatterns = [
]+static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT) # 在url配置的尾部加
=======================================以上是DJango项目的前期工作========================================================
一、数据库的设计(ORM):
思路:分层设计,如下例子
根据app设计(分层设计) uers-用户管理 Course-课程管理 organization-机构和教师管理 以上的3个app在同一层 operation-用户操作管理 (最上层)
1)给每个app的model文件里,建表(关系对象映射):
一般用户表,都需要继承Django原本的auth表
1)可以继承默认生成的user表 from django.contrib.auth.models import AbstractUser class Userprofile(AbstractUser): nick_name = models.CharField(max_length=32,verbose_name=u"昵称",default=u"") birthday = models.DataField(verbose_name=u"生日",null=True,blank=True) gender = models.CharField(choices=(("male",u"男"),("female",u"女")),default=u"") address = models.CharField(max_length=128,default=u"") mobile = models.CharField(max_length=11,null=True,blank=True) image = models.ImageField(upload_to="image/%Y/%m",default=u"image/default.png",max_length=128) class Meta: verbose_name = "用户信息" verbose_name_plural = verbose_name def __unicode__(self): # 定义返回utf8(中文) return self.username 注: a)在setting中增加字段 AUTH_USER_MODEL = "users.Userprofile" # users为app的名,Userprofile为数据表的名 b)下面为一些AbstractBaseUser的子类必须定义的关键的字段和方法: # 否则会报错:type object 'UserProfile' has no attribute 'USERNAME_FIELD' USERNAME_FIELD 必须设置。 设置认证标识,设置成标识的字段 unique=True class MyUser(AbstractBaseUser): identifier = models.CharField(max_length=40, unique=True) ... USERNAME_FIELD = 'identifier' c) 用户重写: http://www.jianshu.com/p/b993f4feff83
又如给Course建表:
1 class Course(models.Model): 2 3 name = models.CharField(max_length=64,verbose_name=u'课程名称') 4 org_name = models.ForeignKey(CourseOrg,verbose_name=u'所属机构',null=True,blank=True) 5 teacher = models.ForeignKey(Teacher,verbose_name=u'课程教师',null=True,blank=True) 6 desc = models.CharField(max_length=256,verbose_name=u'课程描述') 7 is_Adware = models.BooleanField(default=False,verbose_name=u'是否广告') 8 detail = models.TextField(verbose_name=u'课程详情') 9 course_type = models.CharField(verbose_name=u'课程类型',default='',max_length=50) 10 degree = models.IntegerField(default=1,choices=((1,'初级'),(2,'中级'),(3,'高级')),verbose_name=u'课程级别') 11 learn_times = models.IntegerField(default=0,verbose_name=u'学习时长') 12 students = models.IntegerField(default=0,verbose_name=u'学习人数') 13 fav_nums = models.IntegerField(default=0,verbose_name=u'收藏人数') 14 image = models.ImageField(upload_to='course/%Y/%m',default='course/default.png',max_length=256,verbose_name=u'课程图片') 15 click_num = models.IntegerField(default=0,verbose_name=u'点击人数') 16 add_time = models.DateTimeField(default=datetime.now,verbose_name=u'添加时间') 17 teacher_tellyour = models.CharField(default='',verbose_name=u'老师的讲话',max_length=300) 18 work = models.CharField(default='',verbose_name=u'职业',max_length=64)
2)在setting中增加字段
在INSTALLED_APPS列表里加载app
3)给app建好需求的表时:
运行命令:
python manage.py makemigrations # 连接库
python manage.py migrate # 建表
附:(重点注意)
附:1、如果解决数据库输入中文乱码的问题 1)创建数据库时 create database test default charset=utf8; 2)在Django创建数据表示用: python manager.py syncdb # 3.0版本后就没有了 2、每生成一个数据表 1)在app目录下的migrations目录会生成对应一个记录文件 2)在数据库中的auth_migration表里也会生成一条对应的数据记录 3、verbose_name 是后台管理显示的名字 4、def __unicode__(self): return self.name 是后台管理显示创建每条数据的名称 注:上面是Python2.0的版本 3.0版本是 def __str__(self): 5、注:涉及到文件上传的upload # 配置上传文件的路径 ========================================= MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR,"media") =========================================
二、后台管理(Django admin和xadmin)---一般用xadmin,功能更强大,智能的管理系统
特点: 权限管理 少前端样式 快速开发 1、设置网站为中文显示(zh-hans,Asia/Shanghai,USE_TZ=False) # USE_TZ管理写入数据库的时区时间 2、管理user用户表 1)设置app的admin.py ================================================= from .models import UserProfile class UserProfileAdmin(admin.ModelAdmin): pass admin.site.register(UserProfile,UserProfileAdmin) ================================================= 另外一个后台管理构架--xadmin 1、安装 1)pip install xadmin 2)源码安装(推荐使用),用GitHub 依赖包:pip3 install future;pip3 install django-crispy_forms; 3)在setting中的applist加上 'xadmin', 'crispy_forms', 4) 修改url,把admin ==> xadmin 5) 生成xadmin数据表 2、xadmin用法--app的model注册 1)在每个app文件夹里新建py文件:adminx.py 2) import xadmin from .models import UserProfile,EmailVerifyRecord,Banner class EmailVerifyRecordAdmin(object): list_display = ['email','code','send_type','send_time'] # 展示出来的列 search_fields = ['email','code','send_type'] # 搜索栏 list_filter = ['email','code','send_type','send_time'] # 过滤器 ordering = ['-click_nums'] # 排序 readonly_fields = ['fav_nums'] # 设置只读 exclude = ['click_nums'] # 不可见,与readonly_fields有冲突,同一列数据不可同时存在这两个字段里 xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin) # 与admin关联 3)注: 当有外键时: list_filter = ['user__username','fav_id','fav_type','add_time'] # 过滤器有所变量,用双下划线 3、xadmin后台管理页面设置(在user的app设置) 1)主题设置 ============================================== from xadmin import views class BaseSetting(object): enable_themes = True # 显示主题 use_bootswatch = True # 能使用那些主题 xadmin.site.register(views.BaseAdminView,BaseSetting) ============================================== 2)网站标题和页尾标题、model显示样式 ============================================== from xadmin import views class GlobalSetting(object): site_title = "腾飞后台管理系统" site_footer = "腾飞学习网" menu_style = "accordion" xadmin.site.register(views.CommAdminView,GlobalSetting) ============================================== 3)model显示的app名,如何改为中文名 a. 在apps.py修改 在类中增加 verbose_name = u"用户操作" b. 在__init__.py 文件增加 default_app_config = "users.apps.Usersconfig" # Usersconfig :类名 + config
三、每个html页面与一个view的类一一对应
注:
要用“return HttpResponseRedirect(reverse("index"))”而不用“return render(request,"index.html",{})”
原因是:第一种:除了跳转到页面,还会执行“页面对应view方法”,而render不会执行view方法
如:登录与注册的页面设置
一、登录 1、网站主页 1)把index.html 放入templates里 2)新建静态文件/static,把css、images、img、js、media放入其内 3)设置setting中的静态文件路径 4)编写url ================================================== from django.views.generic import TemplateView url(r'^$',TemplateView.as_view(template_name="index.html"),name="index"), ================================================== 2、登录页面 1)把login.html 放入templates里 2)编写user的app中的views文件(写函数)-- 一般是以类的方式写 ================================================== 类: from django.contrib.auth import authenticate,login # 用户验证函数 from django.views.generic.base import View # 编写类时,一定要继承该类 class UserLogin(View): def get(self,request): return render(request, "login.html", {}) def post(self,request): user_name = request.POST.get("username",None) user_pwd = request.POST.get("password",None) result = authenticate(username=user_name,password=user_pwd) # 使字段request.user.is_authenticated变成True if result: login(request,result) # 把result写入request return render(request,"index.html",{"username":result.username}) # result为user该条数据 else: return render(request,"login.html",{"meg":"用户名或密码错误"}) ============================================================ 函数: from django.contrib.auth import authenticate,login # 用户验证函数;login是生成session def UserLogin(request): if request.method == "POST": user_name = request.POST.get("username",None) user_pwd = request.POST.get("password",None) result = authenticate(username=user_name,password=user_pwd) # 使字段request.user.is_authenticated变成True if result: login(request,result) # 把result写入request return render(request,"index.html",{"username":result.username}) else: return render(request,"login.html",{}) elif request.method == "GET": return render(request, "login.html", {}) ============================================================ 注:POST要大写;两个内置用户验证函数authenticate,login 3)编写验证成功后跳转页面的设置(数据交互) {% if request.user.is_authenticated %} # 验证成功后,request.user.is_authenticated为True 4)url url(r'^login/',user_views.UserLogin.as_view(),name="userlogin") # 类的写法,多了as_view() url(r'^login/',user_views.UserLogin,name="userlogin") # 函数的写法 3、自定义authenticate类,用户验证(原来只支持用户名登录,不支持邮箱登录) 1)在app中views文件里重写authenticate ============================================================ from django.contrib.auth.backends import ModelBackend # 重写时一定要继承这个类 from django.db.models import Q # 逻辑and,or class UserBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): try: user = UserProfile.objects.get(Q(username=username)|Q(email=username)) # 或的写法,and的写用逗号隔开 if user.check_password(password): # 检查密码是否正确 return user except Exception as e: return None ============================================================ 2)在setting中加入 AUTHENTICATION_BACKENDS = ( "user.views.UserBackend", ) 4、后台的Form验证(有两种方法) 1)在app下新建form.py,内容如下 ========================================================= 方法一:自定义 from django import forms class LoginForm(forms.Form): username = forms.CharField(required=True) # username必须与form表单里定义的name一致 password = forms.CharField(required=True,min_length=8) # password必须与form表单里定义的name一致 注:重新定义错误信息: error_messages={'required':u"密码不能为空",'invalid':u"密码最少8位数"} 方法二:引用自带的(推荐使用,与model结合) class UserAskForm(forms.ModelForm): class Meta: model = UserAsk fields = ['name','mobile','course_name'] def clean_mobile(self): mobile = self.cleaned_data['mobile'] mobile_model = "^1[358]\d{9}$|^147\d{8}$|^176/d{8}$" p = re.compile(mobile_model) if p.match(mobile): return mobile else: raise forms.ValidationError(u'手机号码错误',code="mobile_invalid") ========================================================= 2)在view里调用 方法一对应的调用: login_form = LoginForm(request.POST) # 获取一个验证的大字典,错误信息放在errors的健值里 if login_form.is_valid(): # 判断是否验证成功 方法二对应的调用: userask_form = UserAskForm(request.POST) if userask_form.is_valid(): # 把提交数据保存在数据表里,不用在从form表单里提出每个数据,在进行保存(优点) userask_obj = userask_form.save(commit=True) 3)在html调用login_form {% if form_error.errors.password %}errorput{% endif %} {% for key,value in form_error.errors.items %} # 字典循环(items) 二、注册 1、注册页面 其他的设置类似于登录页面。 注意: 1)、验证码(相关操作地址http://django-simple-captcha.readthedocs.io/en/latest/usage.html) 2)、验证码在view引用时,get方法时:register_form = RegisterForm() # 不需要传变量 def get(self,request): register_form = RegisterForm() return render(request,'register.html',{"register_form":register_form}) 3)、验证码在html引用是:{{ register_form.captcha }} 2、后台逻辑操作 =============================================== class UserRegister(View): def get(self,request): register_form = RegisterForm() return render(request,'register.html',{"register_form":register_form}) def post(self,request): register_form = RegisterForm(request.POST) if register_form.is_valid(): user_name = request.POST.get("email",None) user_pwd = request.POST.get("password",None) user_object = UserProfile() user_object.username = user_name user_object.email = user_name user_object.password = make_password(user_pwd) user_object.save() return render(request,'index.html',{"username":user_name}) else: return render(request,'register.html',{"register_form":register_form}) =============================================== 3、实现发送邮箱 1)配置setting =========================================== EMAIL_HOST = "smtp.126.com" EMAIL_PORT = 25 EMAIL_HOST_USER = "huang7299@126.com" EMAIL_HOST_PASSWORD = "huang5607299" # 授权密码 EMAIL_USE_TLS = False EMAIL_FROM = "huang7299@126.com" =========================================== 2)在apps下进行目录util,在目录下新建文件sendmail.py ============================================================ import random from user.models import EmailVerifyRecord from django.core.mail import send_mail # django内置发邮件 from TFF.settings import EMAIL_FROM # 加载setting变量 def randow_str(num=6): code = '' for i in range(num): r = random.randrange(0,4) if r == 0 or r == 2: m = random.randint(0,9) code += str(m) else : temp = chr(random.randint(65,90)) code += str(temp) return code def Sendmail(email,send_type=2,code_num=10): email_object = EmailVerifyRecord() code = randow_str(num=code_num) email_object.code = code email_object.email = email email_object.send_type = send_type email_object.save() email_title='' email_boby='' if send_type == 2 : email_title='腾飞学习网注册激活链接' email_boby='请点击下面的链接激活你的账号:http://127.0.0.1:8000/active/{0}'.format(code) email_result = send_mail(email_title,email_boby,EMAIL_FROM,[email]) if email_result: return True else: return False elif send_type == 1 : email_title='腾飞学习网重置密码链接' email_boby='请点击下面的链接重置你的密码:http://127.0.0.1:8000/reset/{0}'.format(code) email_result = send_mail(email_title,email_boby,EMAIL_FROM,[email]) if email_result: return True else: return False ============================================================ 三、忘记密码 ========================================================================= class UserReset(View): def get(self,request,code_num): email_obj = EmailVerifyRecord.objects.filter(code=code_num) for i_email in email_obj: email = i_email.email send_time = i_email.send_time now_time = datetime.datetime.now() days = (now_time - send_time).days seconds = (now_time - send_time).seconds if days == 0 and seconds < 600: user_obj = UserProfile.objects.get(email=email) return render(request,"password_reset.html",{"email_value":user_obj.email}) else: continue return render(request,"sendmail_outtime.html") ========================================================================== class UserPwdReset(View): # def get(self,request): # return render(request,'password_reset.html') def post(self,request): pwdreset_form = PwdResetForm(request.POST) email = request.POST.get('email',None) if pwdreset_form.is_valid(): password1 = request.POST.get('password1',None) password2 = request.POST.get('password2',None) if password1 == password2: user_obj = UserProfile.objects.get(email=email) user_obj.password = make_password(password1) user_obj.save() return render(request,'login.html',{}) else: return render(request,'password_reset.html',{"email_value":email,"mesg":u"密码不一致"}) else: return render(request,'password_reset.html',{"pwdreset_form":pwdreset_form,"email_value":email}) 注:重置密码页面不用写get,避免直接访问 ========================================================================== url(r'^forgetpwd/',user_views.UserForgetPWD.as_view(),name="userforgetpwd"), url(r'^reset/(?P<code_num>.*)/$',user_views.UserReset.as_view(),name="userreset"), url(r'^password_reset/',user_views.UserPwdReset.as_view(),name="pwdreset"), 注:第三条url的目的 # 为了在html上form提交上使用action="{% url 'pwdreset' %}" ========================================================================== 四、退出登录 ========================================================================== class UserLogout(IsLoginRequired,View): def get(self,request): logout(request) from django.core.urlresolvers import reverse return HttpResponseRedirect(reverse("index")) 注:要用“return HttpResponseRedirect(reverse("index"))”而不用“return render(request,"index.html",{})” 原因是:第一种:除了跳转到页面,还会执行“页面对应view方法”,而render不会执行view方法 ============================================================================
四、前端和后台逻辑实现数据交互
一、前端通过http方式提交数据到后台(get,post) 1、html 子form表单下面要加上以下的模板语言,才能把数据提交到后台 {% csrf_token %} 2、view 后台获取数据(注:POST是大写) if request.method == 'POST': # request包含了请求所发给后端的所有信息(请求头,请求方式,数据。。) u_user = request.POST.get('name',None) u_email = request.POST.get('email',None) u_adress = request.POST.get('address',None) u_textinfo = request.POST.get('message',None) models.Userinfo2.objects.create( user=u_user,email=u_email,adress=u_adress,textinfo=u_textinfo ) 二、后台传递数据到前端 1、views 格式: return render(request,html,{}) 例子: return render(request,"upbook.html",{ "messgeinfo":session # session可以是字典形式 }) 2、html(模板语言) <div style="color: red;">{{ messgeinfo.error }}</div> # 字典获取元素的方法messgeinfo.error
1、将上传的图片或者文件,展示到前端 1)配置settings # 上传文件的路径 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR,"media") 2)url配置 ================================================================== 旧版本: from django.views.static import serve # 前端显示出图片内容,旧版本 from TFF.settings import MEDIA_ROOT # url(r'^madia/(?P<path>.*)$', serve,{"document_root":MEDIA_ROOT}), # 旧版本 ================================================================== # 新版本 from django.conf.urls.static import static from TFF import settings urlpatterns = [ ]+static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT) # 在url配置的尾部加 3)html展示 {{ MEDIA_URL }}{{ obj.image }} # obj.image 为用户上传到数据库的字段