zoukankan      html  css  js  c++  java
  • Django 的学习(2) 从adminuser到配置

    创建一个superuser

    python manage.py createsuperuser
    然后是密码邮箱用户名

    You should see a few types of editable content: groups and users. They are provided by django.contrib.auth, the authentication framework shipped by Django.

    这个模块是由
    django.contrib.auth提供的
    将polls 导入admin界面
    修改
    polls/admin.py

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

    注意models.py 和 admin.py在同一目录下,所以就form .models import的形式
    Things to note here:

    The form is automatically generated from the Question model.
    The different model field types (DateTimeField, CharField) correspond to the appropriate HTML input widget. Each type of field knows how to display itself in the Django admin.
    Each DateTimeField gets free JavaScript shortcuts. Dates get a “Today” shortcut and calendar popup, and times get a “Now” shortcut and a convenient popup that lists commonly entered times.

    The bottom part of the page gives you a couple of options:

    Save – Saves changes and returns to the change-list page for this type of object.
    Save and continue editing – Saves changes and reloads the admin page for this object.
    Save and add another – Saves changes and loads a new, blank form for this type of object.
    Delete – Displays a delete confirmation page.
    #add more view#
    首先是polls/views.py
    的修改

    from . import views
    
    urlpatterns = [
        # ex: /polls/
        url(r'^$', views.index, name='index'),
        # ex: /polls/5/
        url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
        # ex: /polls/5/results/
        url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
        # ex: /polls/5/vote/
        url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
    ]
    

    看看正则表达式的用法,比较厉害的是
    (?P<question_id>[0-9]+)
    这里捕获了一个参数,传递给后面的views方法,像这样
    detail(request=, question_id='34')
    这里是真的正则表达式
    所以你可以这么做
    url(r'^polls/latest.html$', views.index),
    但是很傻

    实用的view
    polls/views.py
    from django.http import HttpResponse
    from .models import Question
    def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

    Leave the rest of the views (detail, results, vote) unchanged

    只改变了第一个index的
    注意这里用了Question 来调用数据库中的内容了

    templates 技术
    First, create a directory called templates in your polls directory. Django will look for templates in there.

    Your project’s TEMPLATES setting describes how Django will load and render templates. The default settings file configures a DjangoTemplates backend whose APP_DIRS option is set to True. By convention DjangoTemplates looks for a “templates” subdirectory in each of the INSTALLED_APPS.

    Within the templates directory you have just created, create another directory called polls, and within that create a file called index.html. In other words, your template should be at polls/templates/polls/index.html. Because of how the app_directories template loader works as described above, you can refer to this template within Django simply as polls/index.html.

    Template namespacing

    Now we might be able to get away with putting our templates directly in polls/templates (rather than creating another polls subdirectory), but it would actually be a bad idea. Django will choose the first template it finds whose name matches, and if you had a template with the same name in a different application, Django would be unable to distinguish between them. We need to be able to point Django at the right one, and the easiest way to ensure this is by namespacing them. That is, by putting those templates inside another directory named for the application itself.

    默认情况下django会检查所有install_app 下面的templates
    但是最好再templates下面再加一个与install_app同名的文件,
    因为django会默认的将所有tempaltes合并,这么的话要是有同名文件就无法识别了

    polls/templates/polls/index.html

    {% if latest_question_list %}
        <ul>
        {% for question in latest_question_list %}
            <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>No polls are available.</p>
    {% endif %}
    

    这里使用了templates的语法,不是很懂

    polls/views.py
    from django.http import HttpResponse
    from django.template import loader

    from .models import Question

    def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
    'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))
    这里使用了loader 加载template
    context 字典文件 键应该是变量名的字符串,
    值是变量值
    最后调用加载了template 的loader 的render方法,接受两个参数
    返回
    from django.shortcuts import render

    from .models import Question

    def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)
    也可以这么写,调用 django.shortcuts 中的render
    接受 request template的字符串 context
    可以看出来确实查找templates的时候是合并了每一个installed_app的templates的

    404的产生#

    polls/views.py
    from django.http import Http404
    from django.shortcuts import render

    from .models import Question

    ...

    def detail(request, question_id):
    try:
    question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
    raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})
    可以看到except 搞定了一个异常,紧接着我就抛出了一个django.http 中的
    Http404
    然后就进入了page404

    shortcut
    这里可以使用 django.shortcuts 中的 get_object_or_404 方法
    该方法第一个参数可以是 Django的model
    第二个参数
    nd an arbitrary number of keyword arguments, which it passes to the get() function of the model’s manager. It raises Http404 if the object doesn’t exist.

    Philosophy(哲♂学)
    Why do we use a helper function get_object_or_404() instead of automatically catching the ObjectDoesNotExist exceptions at a higher level, or having the model API raise Http404 instead of ObjectDoesNotExist?
    Because that would couple the model layer to the view layer. One of the foremost design goals of Django is to maintain loose coupling. Some controlled coupling is introduced in the django.shortcuts module.

    There’s also a get_list_or_404() function, which works just as get_object_or_404() – except using filter() instead of get(). It raises Http404 if the list is empty.
    差不多,还记的数据库中对Objects model的操作了吗
    #用templates 的语法来玩耍吧#
    polls/templates/polls/detail.html

    <h1>{{ question.question_text }}</h1>
    <ul>
    {% for choice in question.choice_set.all %}
        <li>{{ choice.choice_text }}</li>
    {% endfor %}
    </ul>
    

    dot-lookup syntax是一个什么鬼,但是django用这种语法
    奇怪的语法,我打错了尖括号与花括号,还查了5分钟
    但是这里不全python的语法
    哪里调用的因该是
    for chocie in question.choice_set.all()
    刚开始的时候,django试图去用 dictionary lookup 结果失败了
    然后使用 attribute lookup 成功了
    如果还失败就会使用 list-index lookup
    Method-calling happens in the {% for %} loop: question.choice_set.all is interpreted as the Python code question.choice_set.all(), which returns an iterable of Choice objects and is suitable for use in the {% for %} tag.

    更换hardcode.
    修改 index 中的hardcode

  • {{ question.question_text }}
  • 利用<% url %> 来达到目的

    在这里指明利用 detail的url 但是有一个参数,就放置在后面
    管理url的名字空间(万一重名咋整啊)
    只需要在 urls.py 中加入app_name就可以了,这里的url文件最好是子文件
    polls/urls.py

    from django.conf.urls import url
    
    from . import views
    
    app_name = 'polls'
    urlpatterns = [
        url(r'^$', views.index, name='index'),
        url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
        url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
        url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
    ]
    

    然后这个url的名称实际上就变成了
    polls:detail
    提交表单的操作

    polls/templates/polls/detail.html

    <h1>{{ question.question_text }}</h1>
    
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    
    <form action="{% url 'polls:vote' question.id %}" method="post">
    {% csrf_token %}
    {% for choice in question.choice_set.all %}
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
    {% endfor %}
    <input type="submit" value="Vote" />
    </form>
    

    每一个选项的值是该选项的id
    名字就是"choice"
    这意味着 提交上去的时候就是 POST data
    'choice'=2
    We set the form’s action to {% url 'polls:vote' question.id %}, and we set method="post". Using method="post" (as opposed to method="get") is very important, because the act of submitting this form will alter data server-side. Whenever you create a form that alters data server-side, use method="post". This tip isn’t specific to Django; it’s just good Web development practice.

    forloop.counter 表示多少次for循环到了这里,一个数字

    修改vote view的代码

    reqeust.POST 是一个像字典的东西,values are alway strings
    Note that Django also provides request.GET for accessing GET data in the same way – but we’re explicitly using request.POST in our code, to ensure that data is only altered via a POST call.

    然而这个POST也会 raise KeyError 的
    After incrementing the choice count, the code returns an HttpResponseRedirect rather than a normal HttpResponse. HttpResponseRedirect takes a single argument: the URL to which the user will be redirected (see the following point for how we construct the URL in this case).

    As the Python comment above points out, you should always return an HttpResponseRedirect after successfully dealing with POST data. This tip isn’t specific to Django; it’s just good Web development practice.

    We are using the reverse() function in the HttpResponseRedirect constructor in this example. This function helps avoid having to hardcode a URL in the view function. It is given the name of the view that we want to pass control to and the variable portion of the URL pattern that points to that view. In this case, using the URLconf we set up in Tutorial 3, this reverse() call will return a string like

    '/polls/3/results/'

    修改view中的reuslts
    一个View不一定用一个模板
    比如view votes 中如果有错误信息就使用detail.html
    否则就是重定向到 另外一个url 对应的 view

    目前为止仍然有缺陷,比如两个人一起点了按钮,那么只会加一次,
    Note

    The code for our vote() view does have a small problem. It first gets the selected_choice object from the database, then computes the new value of votes, and then saves it back to the database. If two users of your website try to vote at exactly the same time, this might go wrong: The same value, let’s say 42, will be retrieved for votes. Then, for both users the new value of 43 is computed and saved, but 44 would be the expected value.

    This is called a race condition. If you are interested, you can read Avoiding race conditions using F() to learn how you can solve this issue.

    Race condition问题

    generic 编程
    polls/urls.py

    from django.conf.urls import url
    
    from . import views
    
    app_name = 'polls'
    urlpatterns = [
        url(r'^$', views.IndexView.as_view(), name='index'),
        url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
        url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'),
        url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
    ]
    

    这里用view.indexView取代了之前的

    注意一下参数这里变成了pk
    class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.order_by('-pub_date')[:5]
    

    class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'

    class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'

    def vote(request, question_id):
    ... # same as above, no changes needed.
    View 也改成这样了
    注意他们都是那个的子类
    而且也没有返回什么httpresponse 估计是在父类中实现了

    name是必须要有的没有的话会自动生成

    每一个generic 需要知道在操作那一个model 这需要用到model attr

    DetailView 需要指定一个 primary key value 从URL中获取 叫做pk
    获取该模型中的哪一个吧

    默认来说,使用的模板应该是 /_detail.html
    就是 polls/Question_detail 所以需要手动指定 template_name

    磨人来说 LIStView 使用/_list.html
    所以需要手工指定一下
    更多generic 的内容请见
    For full details on generic views, see the generic views documentation.

    自动化的测试#

    测试文件放在polls/test.py下是一个好选择

    python manage.py test polls 测试工具
    它寻找django.test.TestCase 的子类进行测试

    并且穿件一个专用的数据库用于测试

    寻找以test开始的所有子方法

    运行test方法,检查assertIs()方法的对错。

    虽然现在app还比较简单,但是它会越来越复杂的

    django提供一个test client 用于测试

    甚至可以再test.py里面 或者 shell 里面

    from django.test.utils import setup_test_environment
    setup_test_environment()

    使用setup_test_envieonment()可以使得 response中的有些attr可用
    比如response.context , 默认来说是不可以的

    但是需要指出的是,这个方法并不会创建数据库,它在已经有的数据库上运行

    from django.test import Client

    create an instance of the client for our use

    client = Client()

    然后就是创建一个client实例(用于测试)

    get a response from '/'

    response = client.get('/')
    Not Found: /

    we should expect a 404 from that address; if you instead see an

    "Invalid HTTP_HOST header" error and a 400 response, you probably

    omitted the setup_test_environment() call described earlier.

    response.status_code
    404

    on the other hand we should expect to find something at '/polls/'

    we'll use 'reverse()' rather than a hardcoded URL

    from django.urls import reverse
    response = client.get(reverse('polls:index'))
    response.status_code
    200

    response.content
    b'

    '

    response.context['latest_question_list']
    <QuerySet [<Question: What's up?>]>
    然后可以进行各种的测试
    ,注意这里我们使用了 reverse 来代替那些hardcode 应该是解析URL使用的

    添加各种测试代码
    polls/tests.py

    def create_question(question_text, days):
        """
        Create a question with the given `question_text` and published the
        given number of `days` offset to now (negative for questions published
        in the past, positive for questions that have yet to be published).
        """
        time = timezone.now() + datetime.timedelta(days=days)
        return Question.objects.create(question_text=question_text, pub_date=time)
    
    
    class QuestionIndexViewTests(TestCase):
        def test_no_questions(self):
            """
            If no questions exist, an appropriate message is displayed.
            """
            response = self.client.get(reverse('polls:index'))
            self.assertEqual(response.status_code, 200)
            self.assertContains(response, "No polls are available.")
            self.assertQuerysetEqual(response.context['latest_question_list'], [])
    
        def test_past_question(self):
            """
            Questions with a pub_date in the past are displayed on the
            index page.
            """
            create_question(question_text="Past question.", days=-30)
            response = self.client.get(reverse('polls:index'))
            self.assertQuerysetEqual(
                response.context['latest_question_list'],
                ['<Question: Past question.>']
            )
    
        def test_future_question(self):
            """
            Questions with a pub_date in the future aren't displayed on
            the index page.
            """
            create_question(question_text="Future question.", days=30)
            response = self.client.get(reverse('polls:index'))
            self.assertContains(response, "No polls are available.")
            self.assertQuerysetEqual(response.context['latest_question_list'], [])
    
        def test_future_question_and_past_question(self):
            """
            Even if both past and future questions exist, only past questions
            are displayed.
            """
            create_question(question_text="Past question.", days=-30)
            create_question(question_text="Future question.", days=30)
            response = self.client.get(reverse('polls:index'))
            self.assertQuerysetEqual(
                response.context['latest_question_list'],
                ['<Question: Past question.>']
            )
    
        def test_two_past_questions(self):
            """
            The questions index page may display multiple questions.
            """
            create_question(question_text="Past question 1.", days=-30)
            create_question(question_text="Past question 2.", days=-5)
            response = self.client.get(reverse('polls:index'))
            self.assertQuerysetEqual(
                response.context['latest_question_list'],
                ['<Question: Past question 2.>', '<Question: Past question 1.>']
            )
    

    在许多的测试代码之中
    好像这个TestCase类中本身就有一个client子类,直接调用就可以了
    而且在这里数据库是隔离的,

    比如 self.client.get(reverse("polls:index"))

    另外返回Object的时候
    #return Question.objects.create(question_text=question_text,pub_date=time)
    return Question(question_text=question_text,pub_date=time)
    确实有区别,后一种并没有吧数据真的写进数据库内,还需要额外的save操作,前一种就直接写入数据库了(虚拟)

    修改detail view 类
    到目前为止,用户可以访问到任何的投票。这不是我们希望的

    目前应该是 通过URL中的pk传参,然后找到model 对应的Question所有中 pk 值得一项

    修改如下
    在view下继承了那个DetailView的子类中添加如下代码
    polls/views.py
    class DetailView(generic.DetailView):
    ...
    def get_queryset(self):
    """
    Excludes any questions that aren't published yet.
    """
    return Question.objects.filter(pub_date__lte=timezone.now())

    这样就圈定了queryset也就是 所有满足条件的,再就访问不了所有的question了

    测试也就是生成一个url 利用client get一下 对照了一status code 是否正确

    测试的好习惯
    a separate TestClass for each model or view
    a separate test method for each set of conditions you want to test
    test method names that describe their function
    进一步的测试支持
    Django includes LiveServerTestCase to facilitate integration with tools like
    Selenium.

    查找dead code
    See Integration with coverage.py for details.

    静态文件的管理#

    That’s what django.contrib.staticfiles is for: it collects static files from each of your applications (and any other places you specify) into a single location that can easily be served in production.

    文件位置的选择#

    Customize your app’s look and feel

    First, create a directory called static in your polls directory. Django will look for static files there, similarly to how Django finds templates inside polls/templates/.

    Django’s STATICFILES_FINDERS setting contains a list of finders that know how to discover static files from various sources. One of the defaults is AppDirectoriesFinder which looks for a “static” subdirectory in each of the INSTALLED_APPS, like the one in polls we just created. The admin site uses the same directory structure for its static files.

    Within the static directory you have just created, create another directory called polls and within that create a file called style.css. In other words, your stylesheet should be at polls/static/polls/style.css. Because of how the AppDirectoriesFinder staticfile finder works, you can refer to this static file in Django simply as polls/style.css, similar to how you reference the path for templates.

    和template 位置的选择类似,我们要选择 polls/static/polls/ 文件夹下才行
    这个配置文件(查找static 文件目录的设置 仍然在 settings.py文件夹下)

    设置文件在 settings.py 的STATICFILES_FINDERS目录下, 默认有一项是
    AppDirectoriesFinder 就会去查找每个app 下的 static 目录

    简单的添加static文件##

    polls/static/polls/style.css
    li a {
    color: green;
    }

    polls/templates/polls/index.html
    {% load static %}

    The {% static %} template tag generates the absolute URL of static files.

    That’s all you need to do for development. Reload http://localhost:8000/polls/ and you should see that the question links are green (Django style!) which means that your stylesheet was properly loaded.

    {% static %}
    自动生成绝对路径

    利用pscp远程传输 我是非常服气的##

    polls/static/polls/style.css
    body {
    background: white url("images/background.gif") no-repeat right bottom;
    }
    加入一个图片,显示出来

    更改定制 admin界面##

    polls/admin.py界面下
    class QuestionAdmin(admin.ModelAdmin):
    fields = ['pub_date', 'question_text']
    注意这里的定制没啥意思,只有一个field,在界面上也只有一个
    admin.site.register(Question, QuestionAdmin)

    class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
    (None, {'fields': ['question_text']}),
    ('Date information', {'fields': ['pub_date']}),
    ]
    这里就有两个界面了
    每个界面用一个元祖表述
    第一个是标题信息
    第二个是一个字典,键是类型,我好像只知道field ,值是字段

    注意这里使用的是fieldsets#

    admin.site.register(Question, QuestionAdmin)

    更改admin change list#

    polls/admin.py
    class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question_text', 'pub_date')
    这个list display决定了那些属性会被列到changlist目录下
    可以靠点击来按照一定次序来排序

    在model中也可以修改
    model Question 中的was_published_recnetly 修改如下
    was_published_recently.admin_order_field = 'pub_date'
    意味着按was_published_recently排序就是按照pubdate排序
    was_published_recently.boolean = True
    显示不显示 圆叉叉(为假就是单词默认)
    was_published_recently.short_description = 'Published recently?'
    changelist 中的短描述
    For more information on these method properties, see list_display.
    还可以排序的,
    在QuestionAdmin中加入这么一行
    list_filter = ['pub_date']
    然后他就出现了一个以 pub_date为filter的栏目 可以筛选
    还有就是
    search_fields = ['question_text']
    顾名思义如前所述

    Now’s also a good time to note that change lists give you free pagination. The default is to display 100 items per page. Change list pagination, search boxes, filters, date-hierarchies, and column-header-ordering all work together like you think they should.

    设置django自己的template system
    找到base_html的位置
    $ python -c "import django; print(django.path)"
    Now create a directory called admin inside templates, and copy the template admin/base_site.html from within the default Django admin template directory in the source code of Django itself (django/contrib/admin/templates) into that directory.、

    修改一下settings.py 中的参数即可
    推荐位置就是manage.py 的同级目录 创建一个templates 下面的admin
    Astute readers will ask: But if DIRS was empty by default, how was Django finding the default admin templates? The answer is that, since APP_DIRS is set to True, Django automatically looks for a templates/ subdirectory within each application package, for use as a fallback (don’t forget that django.contrib.admin is an application).

    Our poll application is not very complex and doesn’t need custom admin templates. But if it grew more sophisticated and required modification of Django’s standard admin templates for some of its functionality, it would be more sensible to modify the application’s templates, rather than those in the project. That way, you could include the polls application in any new project and be assured that it would find the custom templates it needed.

    See the template loading documentation for more information about how Django finds its templates.
    这里说明了一个默认的django项目总是包括 django.contrib.admin 这个app的

查看全文
  • 相关阅读:
    把一件简单的事情做好你就不简单了
    一个经验尚浅的码农五年软件开发的一点自我总结,对工作五年的反思~
    我就是一名房地产经纪人!不是中介,谁能明白我们呢?
    我与父辈的酒局
    郎意难坚,侬情自热(文/王路)
    红灯须硬闯,马路要横穿(文/王路)
    孩子,你慢慢来
    职场六年后的一点点感言
    有幸见到一朵花的绽放
    当你遇到她
  • 原文地址:https://www.cnblogs.com/sfzyk/p/7544786.html
  • Copyright © 2011-2022 走看看