• Python Djiango


    Django是一个用 Python 编写的 Web 框架。Web 框架是一种软件,基于web框架可以开发动态网站,各种应用程序以及服务。它提供了一系列工具和功能,可以解决许多与Web开发相关的常见问题,比如:安全功能,数据库访问,会话,模板处理,URL路由,国际化,本地化,等等

    第一个django项目

    新建一个django project

    创建了一个名为myblog的项目

    django-admin.py startproject mysite

    运行后,我们会看到如下的目录样式:

    mysite
    ├── manage.py
    └── mysite
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py
    
    

    新建app

    一般一个项目有多个app,当然通用的app也可以在多个项目中使用。
    创建一个名为learn的app,注意:先要进入项目目录中(第一个mysite),cd mysite,然后执行下面语句,创建app。

    python manage.py startapp learn

    我们可以看到mysite中多了一个learn文件夹:

    mysite
    └── learn
    |   ├── migrations
    |   |   └── __init__.py
    |   ├── __init__.py
    |   ├── admin.py
    |   ├── app.py
    |   ├── models.py
    |   ├── tests.py
    |   └── views.py
    ├── manage.py
    └── mysite
    
    

    把新定义的app加到settings.py中的INSTALL——APPS中,修改mysite/mysite/settings.py

    INSTALLED_APPS = (
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
    
        'learn',
    )
    
    

    目的:新建的app如果不加到INSTALL——APPS中的话,django就不能自动找到app中的模板文件(learn/templates/下的文件)和静态文件(learn/static/中的文件)。

    定义视图函数(访问页面的内容)

    打开learn/views.py,编写代码:

    #coding:utf-8 
    from django.http import HttpResponse
    
    def index(request):
        return HttpResponse('Hello World!') #用来向浏览器返回内容
    
    

    定义URL

    打开mysite/mysite/urls.py,进行修改:

    from django.conf.urls import url
    from django.contrib import admin
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^$', learn_views.index),
    ]
    
    

    在命令行上运行python manage.py runserver,这样就打开了服务器,然后我们就可以通过浏览器访问了。

    打开浏览器,输入http://127.0.0.1:8000/,就可以看到Hello World!
    注意:如果是要在另一台电脑上访问,要用python manage.py ip:port的形式,比如监听所有ip:

    python manage.py runserver 0.0.0.0:8000
    #监听机器所有ip,8000端口,访问时用电脑的ip代替127.0.0.1
    
    

    视图与网址进阶

    GET方法获取参数

    采用/add/?a=4&b=5这样的方式进行

    1、修改learn/views.py文件

    from django.shortcuts import render
    from django.http import HttpResponse
    def add(request):
        a=request.GET['a']
        b=request.GET['b']
        c=int(a)+int(b)
        return HttpResponse(str(c))
    
    

    注:request.GET 类似于一个字典,更好的办法是用 request.GET.get(‘a’, 0) 当没有传递 a 的时候默认 a 为 0
    2、修改mysite/urls.py文件

    from django.conf.urls import url
    from django.contrib import admin
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^add/$', learn_views.add,name='add'),
    ]
    
    

    打开网址:http://127.0.0.1:8000/add/?a=4&b=5,就可以看到网页上显示了一个9。

    采用/add/3/4/这样的方式进行

    1、修改learn/views.py文件,新定义一个add2函数

    def add2(request,a,b):
        c=int(a)+int(b)
        return HttpResponse(str(c))
    
    

    2、修改mysite/urls.py文件,加上:

    url(r'^add/(d+)/(d+)/$', learn_views.add2, name='add2'),
    
    

    正则表达式中:
    d 代表一个数字,

    • 代表一个或多个前面的字符,
      写在一起 d+ 就是一个或多个数字,用括号括起来的意思是保存为一个子组,每一个子组将作为一个参数,被 views.py 中的对应视图函数接收。

    我们再访问:http://127.0.0.1:8000/add/4/5/,就可以看到和刚才一样的效果,但是现在网址更优雅了。

    URL name详解

    url(…,name=’add’)中name的作用:name可以用于在templates、models、views中得到对应的网址,相当于给网址取了一个名字,只要这个名字不变,地址变了也能通过名字获取到。

    修改learn/views.py文件:

    from django.shortcuts import render
    def index(request):
        return render(request,'home.html') 
    
    

    在learn中新建一个templates文件夹,在该文件夹中新建一个home.html文件,向其中写入:

    <!DOCTYPE html>
    <html>
    <head>
        <title>首页</title>
    </head>
    <body>
        <a href="/add/4/5/">计算 4+5</a>
    </body>
    </html>
    
    

    修改mysite/urls.py文件,加入:

    url(r'^$', learn_views.index, name='home'),
    
    

    运行服务器,访问:http://127.0.0.1:8000/就可以看到一个页面,页面上有一个链接计算 4+5

    问题:这样会写死网址,会在改了网址(正则)后,很多地方都要修改。

    解决:

    reverse接收url中的name作为第一个参数,代码中可以通过reverse()获取对应的网址,只要url的name不改,就不用改代码中的网址

    from django.urls import reverse
    >>>reverse('add2',args=(4,5))
    u'/add/4/5'
    
    

    在网页模板中:

    #不带参数的:
    {% url 'name' %}
    #带参数的:参数可以是变量名
    {% url 'name' 参数 %}
    
    #例如:
    <a href="{% url 'add2' 4 5 %}">link</a>
    #结果
    <a href="/add/4/5/">link</a>
    
    

    当urls.py进行更改时,前提是name不改变,获取的网址也会动态改变:

    url(r'^new_add/(d+)/(d+)/$', learn_views.add2, name='add2'),
    
    

    这时 {% url ‘add2’ 4 5 %} 就会渲染对应的网址成 /new_add/4/5/。用在 views.py 或 models.py 等地方的 reverse函数,同样会根据 name 对应的url获取到新的网址。

    用旧的url访问现在新的网址,在views.py中写一个跳转方法:

    from django.http import HttpResponseRedirect
    from django.urls import reverse
    
    def old_add2_redirect(request, a, b):
        return HttpResponseRedirect(
            reverse('add2', args=(a, b))
        )
    
    

    urls.py中

    url(r'^add/(d+)/(d+)/$', learn_views.old_add2_redirect),
    url(r'^new_add/(d+)/(d+)/$', learn_views.add2, name='add2'),
    
    

    这样,假如有/add/4/5/,访问时就会自动跳转到新的/new_add/4/5/ 了

    模板

    之前大部分是用HttpResponse来把内容显示到网页上,现在这部分是使用渲染模板的方法来显示内容,我们在前面也接触过。

    在app目录下新建一个templates文件夹,里面新建一个home.html文件,默认配置下,django的模板系统会自动找到app下面的templates文件夹中的模板文件。

    加入通用文件

    网站模板的设计,一般的,我们做网站的一些通用的部分,比如:导航,底部。

    {% extends 'base.html' %} #继承或者扩展原来的base.html
    {% include 'nav.html' %} #这样就加入了通用文件
    {% block content %}
    <div>这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。</div>
    {% endblock %}
    
    

    不同app中模板文件名称冲突

    模板一般放在app下的templates中,Django会自动去这个文件夹中找。但是假如我们每个app的templates中都有一个 index.html,当我们在views.py中使用的时候,直接写一个 render(request, ‘index.html’),Django 能不能找到当前 app 的 templates 文件夹中的 index.html 文件夹呢?

    模板查找机制:Django 查找模板的过程是在每个app的templates文件夹中找(而不只是当前 app 中的代码只在当前的 app 的 templates 文件夹中找)。各个app的templates形成一个文件夹列表,Django遍历这个列表,一个个文件夹进行查找,当在某一个文件夹找到的时候就停止,所有的都遍历完了还找不到指定的模板的时候就是 Template Not Found(过程类似于Python找包)。这样设计有利当然也有弊,有利是的地方是一个app可以用另一个app的模板文件,弊是有可能会找错了。所以我们使用的时候在templates中建立一个app同名的文件夹,这样就好了。

    这就需要把每个app中的 templates 文件夹中再建一个以 app 的名称为名字的文件夹,仅和该app相关的模板放在 app/templates/app/ 目录下面。访问时加上app,如:app/index.html。

    django模板标签

    for循环和list内容的显示

    views.py,在视图中传递了一个list到模板home.html

    def home(request):
        classList=['html','css','jquery']
        return render(request,'home.html',{'classList':classList})
    
    

    home.html,在模板中这样使用它:

    教程列表:
    {% for i in classList %}
    {{ i }}
    {% endfor %}
    
    

    for循环要有一个结束标记,上面的代码假如对应首页的网址,则会显示:教程列表:html css jquery

    知识点:一般的变量之类的用{{}}(变量),功能类的,比如循环,条件判断是用{% %}(标签)

    显示字典中内容

    views.py

    def home(request):
        dict={'name':u'文学','age':'21'}
        return render(request,'home.html',{'dict':dict})
    
    

    home.html

    姓名:{{dict.name}}年龄:{{dict.age}}
    
    

    还可以这样遍历字典:

    {% for key,value in dict.items %}
        {{ key }}:{{value}}
    {% endfor %}
    
    

    for循环中的条件判断

    home.html

    {% for i in classList %}
        {{ i }}{% if not forloop.last %},{% endif %}
    {% endfor %}
    
    

    结果:html,css,jquery

    在for循环中还有很多有用的东西,如下:

    | forloop.counter | 索引从1开始算 |
    | forloop.counter0 | 索引从0开始算 |
    | forloop.revcounter | 索引从最大长度到1 |
    | forloop.revcounter0 | 索引从最大长度到0 |
    | forloop.first | 当遍历的元素为第一项时为真 |
    | forloop.last | 当遍历的元素为最后一项时为真 |
    | forloop.parentloop | 用在嵌套的for循环中,获取上一层for循环的forloop |

    当列表中可能为空值时用for empty

    {% for a in alist %}
        {{a.name}}
    {% empty %}
        列表为空
    {% endfor %}
    
    

    模板上得到视图对应的网址

    {% url 'add' 4 5 %}
    
    

    结果:/add/4/5,就算以后修改网址,只要不修改name,就不用修改模板。

    还可以使用as语句取别名:

    {% url 'add' 4 5 as the_url %}
    <a href="{{the_url}}">链接到:{{the_url}}</a>
    
    

    模板中的逻辑操作

    ==、!=、>=、<=、>、<这些都可以在模板中使用
    {% if var >= 90 %}
    成绩优秀,教室你没少去吧!学得不错
    {% elif var >= 80 %}
    成绩良好
    {% elif var >= 70 %}
    成绩一般
    {% elif var >= 60 %}
    需要努力
    {% else %}
    不及格啊,大哥!多去教室学习啊!
    {% endif %}
    
    
    and、or、not、in、not in也可以在模板中使用
    {% if num <= 100 and num >= 0 %}
    num在0到100之间
    {% else %}
    数值不在范围之内!
    {% endif %}
    
    {% if 'wenxue' in nameList %}
    文学在名单中
    {% endif %}
    
    

    在模板中获取当前网址,当前用户等

    如果不是在views.py中用的render函数,是render_to_response的话,需要将request加入到上下文渲染器。

    修改settings.py

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    ...
                        'django.template.context_processors.request',
                    ...
                ],
            },
        },
    ]
    
    

    然后在模板中我们就可以用request了,一般情况下,推荐使用render而不是render_to_response。

    获取当前用户
    {{request.user}}
    
    

    如果登录就显示内容,不登录就不显示内容:

    {% if request.user.is_authenticated %}
        {{ request.user.username }},您好!
    {% else %}
        请登录,这里放登录链接
    {% endif %
    
    
    获取当前网址
    {{request.path}}
    
    
    获取当前get参数
    {{request.GET.urlencode}}
    
    
    使用
    <a href="{{ request.path }}?{{ request.GET.urlencode }}&delete=1">当前网址加参数 delete</a>
    
    

    模型(数据库)

    django模型是与数据库相关的,与数据库相关的代码一般写在models.py中,django支持sqlite3、mysql、postgresql等数据库,只需要在settings.py中配置即可,不用更改models.py中的代码,丰富的API极大的方便了使用。

    使用数据库

    1. 新建一个项目:django-admin.py startproject learn_models

    2. 进入项目中:cd learn_models

    3. 新建一个应用:python manage.py startapp people

    4. 将项目加入settings.py的INSTALLED_APPS中,也就是告诉django有这么一个应用:

      INSTALLED_APPS=(
          ...
          'people',
      )
      
      
    5. 打开people/models.py文件,修改代码:新建了一个People类,继承自model.Model,一个人有姓名和年龄,这里用到了两种Field。

      from django.db import models
      
      class Person(models.Model):
          name=models.CharField(max_length=30)
          age=models.IntegerField()
      
      
    6. 同步一下数据库(默认使用sqlite3,无需配置)

      python manage.py makemigrations
      python manage.py migrate
      
      
    7. 使用表

      python manage.py shell      #使用该代码,而不是直接使用python,那样会报错
      
      >>>from people.models import Person
      >>>Person.objects.create(name='wenxue',age=21)
      <Person: Person object>
      >>>Person.objects.get(name='wenxue')
      <Person: Person object>
      
      

    上面的查询结果,并没有获取该对象相关信息,所以并不好,所以我们要修改people/models.py,加入函数:

    def __unicode__(self):
        return self.name
    
    结果:<Person: wenxue>
    
    

    获取对象

    Person.objects.all()  
    Person.objects.all()[:10] #切片操作,获取10个人,不支持负索引,切片可以节约内存
    
    Person.objects.get(name=name) #get是用来获取一个对象的,如果需要获取满足条件的一些人,就要用到filter
    
    

    filter是找出满足条件的,当然也有排除符合某条件的

    Person.objects.filter(name="abc") # 等于Person.objects.filter(name__exact="abc") 名称严格等于 "abc" 的人    
    Person.objects.filter(name__iexact="abc") # 名称为 abc 但是不区分大小写,可以找到 ABC, Abc, aBC,这些都符合条件
    Person.objects.filter(name__contains="abc") # 名称中包含 "abc"的人
    Person.objects.filter(name__icontains="abc") #名称中包含 "abc",且abc不区分大小写   
    Person.objects.filter(name__regex="^abc") # 正则表达式查询
    Person.objects.filter(name__iregex="^abc")# 正则表达式不区分大小写
    
    Person.objects.exclude(name__contains="WZ") # 排除包含 WZ 的Person对象
    
    Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名称含有abc, 但是排除年龄是23岁的
    
    

    自定义field

    减少文本的长度,保存数据的时候压缩,读取数据的时候解压缩,如果发现压缩后更长,就用晕文本直接存储

    #coding:utf-8
    from django.db import models
    
    class CompressedTextField(models.TextField):
        """
        model Fields for storing text in a compressed format (bz2 by default)
        """
    
        def from_db_value(self, value, expression, connection, context):
            if not value:
                return value
            try:
                return value.decode('base64').decode('bz2').decode('utf-8')
            except Exception:
                return value
    
        def to_python(self, value):
            if not value:
                return value
            try:
                return value.decode('base64').decode('bz2').decode('utf-8')
            except Exception:
                return value
    
        def get_prep_value(self, value):
            if not value:
                return value
            try:
                value.decode('base64')
                return value
            except Exception:
                try:
                    return value.encode('utf-8').encode('bz2').encode('base64')
                except Exception:
                    return value
    
    

    to_python函数用于转化数据库中的字符到Python的变量,get_prep_value 用于将Python变量处理后(此处为压缩)保存到数据库,使用和Django自带的 Field 一样。from_db_value 函数用于转化数据库中的字符到 Python的变量。

    比如保存一个列表到数据库中,在读取的时候要是python列表的形式,我们来自己写一个ListField

    from django.db import models
    import ast
    
    class ListField(models.TextField):
        __metaclass__ = models.SubfieldBase
        description = "Stores a python list"
    
        def __init__(self, *args, **kwargs):
            super(ListField, self).__init__(*args, **kwargs)
    
        def to_python(self, value):
            if not value:
                value = []
    
            if isinstance(value, list):
                return value
    
            return ast.literal_eval(value)
    
        def get_prep_value(self, value):
            if value is None:
                return value
    
            return unicode(value) # use str(value) in Python 3
    
        def value_to_string(self, obj):
            value = self._get_val_from_obj(obj)
            return self.get_db_prep_value(value)
    
    

    使用时很简单,首先是导入ListField,像自带的Field一样使用:

    class Article(models.Model):
        labels = ListField()
    
    

    运行:

    >>> from app.models import Article
    >>> d = Article()
    >>> d.labels
    []
    >>> d.labels = ["Python", "Django"]
    >>> d.labels
    ["Python", "Django"]
    
    

    数据表更改

    更改数据表后,需要执行代码:

    python manage.py makemigrations
    python manage.py migrate
    
    

    同步,这两行命令会对models.py进行检测,自动发现需要更改的,应用到数据表中。

    在原理的类上增加字段或者删除字段:

    python manage.py sql appname
    
    

    QuerySet API

    QuerySet是数据库接口相关的接口,从数据库中查询出来的结果一般是一个集合,这个集合叫做QuerySet。

    from django.db import models
    class Author(models.Model):
        name=models.CharField(max_length=50)
        email=models.EmailField()
    
        def __unicode__(self):
            return self.name
    
    

    QuerySet创建对象

    #方法1
    Author.objects.create(name='wenxue',email='wenxue@163.com')
    #方法2
    wx=Author(name='wenxue',email='wenxue@163.com')
    wx.save()
    #方法3
    wx=Author()
    wx.name='wenxue'
    wx.email('wenxue@163.com')
    wx.save()
    #方法4
    Author.objects.get_or_create(name='wenxue',email='wenxue@163.com')
    #返回值(object,True/False)
    
    

    获取对象的方法

    见上部分

    QuerySet是可迭代的

    as=Author.objects.all()
    for a in as:
        print a.name
    
    
    小知识

    1、检查Author中是否有对象:Author.objects.all().exists()

    2、得到Author的数量:len(as),但是推荐使用Author.objects.count()来查询数量,它是用sql:select count(*)

    3、将QuerySet变成列表:list(as)

    4、QuerySet用pickle序列化到硬盘再读取出来

    import pickle
    query=pickle.load(s) #s is the pickle string
    qs=MyModel.objects.all()
    qs.query=query #restore the original 'query'
    
    

    5、QuerySet查询结果排序

    Author.objects.all().order_by('name')#按照名称排序
    Author.objects.all().order_by('-name')#实现倒序
    
    

    6、QuerySet支持链式查询

    Author.objects.filter(name__contains='wen').filter(email='wenxue@163.com')
    Author.objects.filter(name__contains='wen').exclude(email='wenxue@163.com')
    
    

    7、QuerySet不支持负索引。解决:

    Author.objects.all().reverse()[:2]#最后两条
    
    Author.objects.order_by('-id')[:2]#id最大的2条
    
    

    8、QuerySet重复的问题

    as=as.distinct()
    
    
    小知识进阶

    1、查看Django QuerySet执行的sql:print str(Author.objects.all().query)

    2、values_list获取元组形式结果:

    as=Author.objects.values_list('name','email')
    list(as)#元组的列表
    
    #如果只需要1个字段,可以指定flat=true
    as=Author.objects.values_list('name',flat=True)
    
    

    3、values获取字典形式的结果

    as=Author.objects.values('name','email')
    list(as)#字典的列表
    
    

    4、extra实现别名,条件,排序等

    extra中可实现别名、条件、排序等,后面两个用filter,exclude一般都能实现,排序用order_by也能实现。主要看别名:

    要执行select name as tag_name from blog_tag语句
    tags=Tag.ojects.all().extra(select={'tag_name':'name'})
    #如果只想其中一个能用,可以用defer排除原来的name
    ...extra(...).def('name')
    
    

    5、annotate聚合计数、求和、平均数等

    a、查询每个作者的文章数
    Article.objects.all().values('author_id').annotate(count=Count('author')).values('author_id','count')
    执行的sql语句
    SELECT author_id, COUNT(author_id) AS count FROM blog_article GROUP BY author_id
    b、求一个作者的所有文章的得分平均值
    Article.objects.values('author_id').annotate(avg_score=Avg('score')).values('author_id', 'avg_score')
    执行的sql语句SELECT "blog_article"."author_id", AVG("blog_article"."score") AS "avg_score" FROM "blog_article" GROUP BY "blog_article"."author_id"
    求总分则是Sum('score')
    
    

    6、select_related优化一对一,多对多查询

    修改settings.py让Django打印出在数据库中执行的语句

    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'console': {
                'class': 'logging.StreamHandler',
            },
        },
        'loggers': {
            'django.db.backends': {
                'handlers': ['console'],
                'level': 'DEBUG' if DEBUG else 'INFO',
            },
        },
    }
    
    

    这样在DEBUG为True的时候,我们可以看出django执行了什么语句

    查询文章的时候连同作者一起查询出来,“文章”和“作者”的关系就是多对一

    articles = Article.objects.all().select_related(‘author’)[:10]

    >>>a1 = articles[0]  # 取第一篇
    
    >>>al.title
    
    >>>al.author.name
    
    

    7、prefetch_related 优化一对多,多对多查询
    和 select_related 功能类似,但是实现不同。select_related 是使用 SQL JOIN 一次性取出相关的内容。prefetch_related 用于 一对多,多对多 的情况,这时 select_related 用不了,因为当前一条有好几条与之相关的内容。

    prefetch_related是通过再执行一条额外的SQL语句,然后用 Python 把两次SQL查询的内容关联(joining)到一起。我们来看个例子,查询文章的同时,查询文章对应的标签。“文章”与“标签”是多对多的关系。

    >>>articles = Article.objects.all().prefetch_related('tags')[:10]
    
    >>>for a in articles:
        print a.title, a.tags.all()#一次性查出了所有相关的内容。
    
    

    8、defer排除不需要的字段

    在复杂的情况下,表中可能有些字段内容非常多,取出来转化成 Python 对象会占用大量的资源。这时候可以用 defer 来排除这些字段,比如我们在文章列表页,只需要文章的标题和作者,没有必要把文章的内容也获取出来(因为会转换成python对象,浪费内存)

    Article.objects.all().defer('content')# 注意这里没有查 content 字段了
    
    

    9、only仅选择需要的字段

    和 defer 相反,only 用于取出需要的字段,假如我们只需要查出 作者的名称

    Author.objects.all().only('name')
    
    

    原生的 SQL 查询(注意:原生查询必须包含主键)

    authors =  Author.objects.raw('select id,name from blog_author limit 1')
    
    

    10、自定义聚合功能
    前面看到了 django.db.models 中有 Count, Avg, Sum 等,但是有一些没有的,比如 GROUP_CONCAT,它用来聚合时将符合某分组条件(group by)的不同的值,连到一起,作为整体返回。我们来演示一下,如何实现 GROUP_CONCAT 功能。

    新建一个文件 比如 my_aggregate.py

    from django.db.models import Aggregate, CharField
    class GroupConcat(Aggregate):
        function = 'GROUP_CONCAT'
        template = '%(function)s(%(distinct)s%(expressions)s%(ordering)s%(separator)s)'
    
        def __init__(self, expression, distinct=False, ordering=None, separator=',', **extra):
            super(GroupConcat, self).__init__(
                expression,
                distinct='DISTINCT ' if distinct else '',
                ordering=' ORDER BY %s' % ordering if ordering is not None else '',
                separator=' SEPARATOR "%s"' % separator,
                output_field=CharField(),
                **extra        )
    
    

    使用时先引入 GroupConcat 这个类,比如聚合后的错误日志记录有这些字段 time, level, info。我们想把 level, info 一样的 聚到到一起,按时间和发生次数倒序排列,并含有每次日志发生的时间。

    ErrorLogModel.objects.values('level', 'info').annotate(
        count=Count(1), time=GroupConcat('time', ordering='time DESC', separator=' | ')
    ).order_by('-time', '-count')
    
    

    Django后台

    新建一个账户

    python manage.py createsuperuser
    
    

    修改admin.py

    进入blog文件夹,修改admin.py文件:

    from django.contrib import admin
    from .models import Person
    
    admin.site.register(Person)
    
    

    只需要这样,我们就有了一个强大的后台

    打开开发服务器

    python manager.py runserver
    
    

    访问http://localhost:8000/admin/,输入设定的账号和密码,就可以看到我们的后台管理界面,上面有默认的Auth,和我们建立的应用blog
    ,blog中包含我们在这个应用中的数据表格,我们可以进行管理,这就是django为我们提供的强大后台。
    [图片上传失败...(image-b5f4c3-1529227685937)]

    注意:要在models中添加函数:

    def __unicode__(self):
        return self.name
    
    

    否则表中的每条数据名称都看起来一样,不能区分(或str函数)。

    在列表中显示与字段相关的内容

    from django.contrib import admin
    from .models import Person
    
    class PersonAdmin(admin.ModelAdmin):
        list_display=('name','age')
    admin.site.register(Person,PersonAdmin)
    
    

    这样就会在列表中显示name和age

    修改django admin site

    定制加载的列表

    根据不同的人显示不同的内容列表,比如输入员只能看见自己输入的,审核员能看到所有的草稿,这时候就需要重写get_queryset方法。

    class MyModelAdmin(admin.ModelAdmin):
        def get_queryset(self, request):
            qs = super(MyModelAdmin, self).get_queryset(request)
            if request.user.is_superuser:
                return qs
            else:
                return qs.filter(author=request.user)
    
    

    该类实现的功能是如果是超级管理员就列出所有的,如果不是,就仅列出访问者自己相关的。

    定制搜索功能

    class PersonAdmin(admin.ModelAdmin):
        list_display = ('name', 'age')
        search_fields = ('name',)
    
        def get_search_results(self, request, queryset, search_term):
            queryset, use_distinct = super(PersonAdmin, self).get_search_results(request, queryset, search_term)
            try:
                search_term_as_int = int(search_term)
                queryset |= self.model.objects.filter(age=search_term_as_int)
            except:
                pass
            return queryset, use_distinct
    
    

    queryset 是默认的结果,search_term 是在后台搜索的关键词。

    修改保存时的一些操作

    可以检查用户,保存的内容等,比如保存时加上添加人。

    from django.contrib import admin
    
    class ArticleAdmin(admin.ModelAdmin):
        def save_model(self, request, obj, form, change):
            obj.user = request.user
            obj.save()
    
    

    其中obj是修改后的对象,form是返回的表单(修改后的),当新建一个对象时 change = False, 当修改一个对象时 change = True。如果需要获取修改前的对象的内容可以用:

    from django.contrib import admin
    
    class ArticleAdmin(admin.ModelAdmin):
        def save_model(self, request, obj, form, change):
            obj_original = self.model.objects.get(pk=obj.pk)
            obj.user = request.user
            obj.save()
    
    

    那么又有问题了,这里如果原来的obj不存在,也就是如果我们是新建的一个怎么办呢,这时候可以用try,except的方法尝试获取,当然更好的方法是判断一下这个对象是新建还是修改,是新建就没有 obj_original,是修改就有:

    from django.contrib import admin
    
    class ArticleAdmin(admin.ModelAdmin):
        def save_model(self, request, obj, form, change):
            if change:# 更改的时候
                obj_original = self.model.objects.get(pk=obj.pk)
            else:# 新增的时候
                obj_original = None
    
            obj.user = request.user
            obj.save()
    
    

    删除时做一些处理

    from django.contrib import admin
    
    class ArticleAdmin(admin.ModelAdmin):
        def delete_model(self, request, obj):
            """
            Given a model instance delete it from the database.
            """
            # handle something here
            obj.delete()
    
    

    django表单

    django的表单forms

    在app中新建一个forms.py文件

    from django import forms
    class AddForm(forms.Form):
        a=forms.IntegerField()
        b=forms.IntegerField()
    
    

    视图函数views.py中

    from django.shortcuts import render
    from django.http import HttpResponse
    #引入我们创建的表单类
    from .forms import AddForm
    
    def index(request):
        if request.method=='post':#当提交表单时
            form=AddForm(request.Post)
            if form.is_valid():#如果提交的数据合法
                a=form.cleaned_data['a']
                b=form.cleaned_data['b']
                return HttpResponse(str(int(a)+int(b)))
        else:#当正常访问时
            form=AddForm()
        return render(request,'index.html',['form':form])
    
    

    对应的模板文件index.html

    <form method='post'>
        {% csrf_token %}
        {{form}}
        <input type='submit' value='提交'/>
    </form>
    
    

    在urls.py中增加这个函数:

    url(r'^$', views.index, name='home'),
    
    

    叙说整个过程:当第一次访问http://localhost:8000/时,执行index函数中的else部分,然后根据表单类AddForm,生成表单元素,渲染到index.html页面,我们看到的效果是两个标签label、两个文本框和一个提交按钮。在我们在输入框中输入数据后,依然是传递到views.py的index函数,然后去执行if部分,获取传送的数据,进行计算,最后响应给用户。

    django配置

    settings.py文件

    file 这个变量可以获取到当前文件(包含这个代码的文件)的路径,os.path.dirname(file) 得到文件所在目录,再来一个os.path.dirname()就是目录的上一级,BASE_DIR 即为 项目 所在目录。

    import os
    BASE_DIR = os.path.dirname(os.path.dirname(__file__))
    
    

    DEBUG=True 时,如果出现 bug 便于我们看见问题所在,但是部署时最好不要让用户看见bug的详情,可能一些不怀好心的人攻击网站,造成不必要的麻烦。

    DEBUG = True
    TEMPLATE_DEBUG = True   
    
    

    ALLOWED_HOSTS 允许你设置哪些域名可以访问,即使在 Apache 或 Nginx 等中绑定了,这里不允许的话,也是不能访问的。当 DEBUG=False 时,这个为必填项,如果不想输入,可以用 ALLOW_HOSTS = [‘*’] 来允许所有的。

    ALLOWED_HOSTS = ['*.besttome.com','www.ziqiangxuetang.com']
    
    

    static 是静态文件所有目录,比如 jquery.js, bootstrap.min.css 等文件。

    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR,'static')
    
    

    一般来说我们只要把静态文件放在 APP 中的 static 目录下,部署时用 python manage.py collectstatic 就可以把静态文件收集到(复制到) STATIC_ROOT 目录,但是有时我们有一些共用的静态文件,这时候可以设置 STATICFILES_DIRS 另外弄一个文件夹,如下:

    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, "common_static"),
        '/var/www/static/',
    )
    #这样我们就可以把静态文件放在 common_static 和 /var/www/static/中了,Django也能找到它们。
    
    

    media文件夹用来存放用户上传的文件,与权限有关。

    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(BASE_DIR,'media')
    
    

    有时候有一些模板不是属于app的,比如 baidutongji.html, share.html等。这样 就可以把模板文件放在 templates 和 templates2 文件夹中了。

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [
                os.path.join(BASE_DIR,'templates').replace('\', '/'),
                os.path.join(BASE_DIR,'templates2').replace('\', '/'),
            ],
            'APP_DIRS': True,
        },
    ]
    
    

    静态文件

    settings.py中静态文件位置

    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.8/howto/static-files/
    
    STATIC_URL = '/static/'
    
    # 当运行 python manage.py collectstatic 的时候
    # STATIC_ROOT 文件夹 是用来将所有STATICFILES_DIRS中所有文件夹中的文件,以及各app中static中的文件都复制过来
    # 把这些文件放到一起是为了用apache等部署的时候更方便
    STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
    
    # 其它 存放静态文件的文件夹,可以用来存放项目中公用的静态文件,里面不能包含 STATIC_ROOT
    # 如果不想用 STATICFILES_DIRS 可以不用,都放在 app 里的 static 中也可以
    STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "common_static"),
    '/path/to/others/static/',  # 用不到的时候可以不写这一行
    )
    
    # 这个是默认设置,Django 默认会在 STATICFILES_DIRS中的文件夹 和 各app下的static文件夹中找文件
    # 注意有先后顺序,找到了就不再继续找了
    STATICFILES_FINDERS = (
        "django.contrib.staticfiles.finders.FileSystemFinder",
        "django.contrib.staticfiles.finders.AppDirectoriesFinder"
    )
    
    

    目录结构

    静态文件放在对应的 app 下的 static 文件夹中 或者 STATICFILES_DIRS 中的文件夹中。当 DEBUG = True 时,Django 就能自动找到放在里面的静态文件。(Django 通过 STATICFILES_FINDERS 中的“查找器”,找到符合的就停下来,寻找的过程 类似于 Python 中使用 import xxx 时,找 xxx 这个包的过程)。

    myblog
    ├── blog
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── migrations
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── static # 应用 blog 下的 static, 默认会找这个文件夹
    │   │   └── 【zqxt.png】
    │   ├── tests.py
    │   │
    │   └── views.py
    ├── common_static # 已经添加到了 STATICFILES_DIRS 的文件夹
    │   └── js
    │       └── 【jquery.js】
    │
    ├── myblog
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    └── manage.py
    
    

    部署时

    收集静态文件。它就会把以前放在app下static中的静态文件全部拷贝到 settings.py 中设置的 STATIC_ROOT 文件夹中

    python manage.py collectstatic
    

    本文转载自https://blog.csdn.net/qq_31792281/article/details/70473739

  • 相关阅读:
    JDBC事务管理
    JDBC常见操作
    Java集合之List接口
    Nginx+Keepalived+Lvs实现双机热备
    Nginx+Consul+Upsync实现动态负载均衡
    DNS域名解析概念
    WPF中实现两个窗口之间传值
    C# 重写(override)和覆盖(new)
    C# DateTime.Now函数
    WPF中在后台实现控件样式
  • 原文地址:https://www.cnblogs.com/karkash/p/12331327.html
Copyright © 2020-2023  润新知