zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    我的WCF之旅(1):创建一个简单的WCF程序
    与众不同 windows phone (15) Media(媒体)之后台播放音频
    与众不同 windows phone (14) Media(媒体)之音频播放器, 视频播放器, 与 Windows Phone 的音乐和视频中心集成
    与众不同 windows phone (10) Push Notification(推送通知)之推送 Tile 通知, 推送自定义信息
    与众不同 windows phone (17) Graphic and Animation(画图和动画)
    与众不同 windows phone (5) Chooser(选择器)
    与众不同 windows phone (26) Contacts and Calendar(联系人和日历)
    与众不同 windows phone (7) Local Database(本地数据库)
    与众不同 windows phone (19) Device(设备)之陀螺仪传感器, Motion API
    与众不同 windows phone (16) Media(媒体)之编辑图片, 保存图片到相册, 与图片的上下文菜单“应用程序...”和“共享...”关联, 与 Windows Phone 的图片中心集成
  • 原文地址:https://www.cnblogs.com/m0488/p/12331327.html
Copyright © 2011-2022 走看看