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
User表

  又如给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
    2import 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 为用户上传到数据库的字段
上传文件或图片的显示

 

posted @ 2017-10-19 16:50  浪子斌  阅读(4704)  评论(0编辑  收藏  举报