Django学习笔记
参考书籍:《Django 3 By Example Build powerful and reliable Python web applications from scratch》 《Django 基础教程 by Leif Azzopardi David Maxwell (z-lib.org)(中译版)》 参考网站:https://docs.djangoproject.com/zh-hans/5.0/
理解Django基本的运行流程 上面我们提到了,Django设计模式是”模型-视图-模板”,具体来说,它们三兄弟的天赋点各不相同:
模型:定义数据 的单元,位于models.py
视图:实现前后端互动响应,同时生成并传递上下文字典 ,位于views.py
模板:即前端代码 ,在Django前期学习中一般都是html代码,位于/project_name/template/app_name目录中(这里project_name和app_name都是指具体的项目和应用名)
这三兄弟联动,其实就可以抽象成后端-前后端交互-前端的框架,当然实际上后端还要和数据库交互,不过在Django中这不是我们想关注的细节
实例讲解 定义模型以及数据 说了这么多,我们用具体的例子来讲解一下,理解之后你会发现,其实Django前期的很多东西都是这个思路,其他诸如相对url,正则表达式,模板标签其实需要的时候problem-driven learning就可以好,接下来我们定义两个模型,用于我们元数据的定义 @rango/models.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from django.db import modelsfrom django.template.defaultfilters import slugifyclass Category (models.Model): name=models.CharField(max_length=128 ,unique=True ) views=models.IntegerField(default=0 ) likes=models.IntegerField(default=0 ) slug = models.SlugField(default='' ,blank=True ) def save (self, *args, **kwargs ): self.slug = slugify(self.name) super (Category, self).save(*args, **kwargs) class Meta : verbose_name_plural='Categories' def __str__ (self ): return self.name class Page (models.Model): category=models.ForeignKey(Category,models.CASCADE) title=models.CharField(max_length=128 ) url=models.URLField() views=models.IntegerField(default=0 ) def __str__ (self ): return self.title
这里我们可以先把一对多,super,Meta之类可能看起来一头雾水的东西放在一边,关注数据本身,其中Category有name,views,likes,slug几种属性,Page有title,url,views几种属性,同时我们通过ForeignKey实现了一个Category对应多个Pages(但这里还没指定谁对应谁,后面会实现)
导入数据 在项目根目录,创立一个名为populate_rango.py的文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 import osos.environ.setdefault('DJANGO_SETTINGS_MODULE' , 'tango_with_django_project.settings' ) import djangodjango.setup() from rango.models import Category, Pagedef populate (): python_pages = [ {"title" : "Official Python Tutorial" , "url" :"http://docs.python.org/2/tutorial/" }, {"title" :"How to Think like a Computer Scientist" , "url" :"http://www.greenteapress.com/thinkpython/" }, {"title" :"Learn Python in 10 Minutes" , "url" :"http://www.korokithakis.net/tutorials/python/" } ] django_pages = [ {"title" :"Official Django Tutorial" , "url" :"https://docs.djangoproject.com/en/1.9/intro/tutorial01/" }, {"title" :"Django Rocks" , "url" :"http://www.djangorocks.com/" }, {"title" :"How to Tango with Django" , "url" :"http://www.tangowithdjango.com/" } ] other_pages = [ {"title" :"Bottle" , "url" :"http://bottlepy.org/docs/dev/" }, {"title" :"Flask" , "url" :"http://flask.pocoo.org" } ] cats = { "Python" : {"pages" : python_pages ,"views" :128 ,"likes" :64 }, "Django" : {"pages" : django_pages ,"views" :64 ,"likes" :32 }, "Other Frameworks" : {"pages" : other_pages ,"views" :32 ,"likes" :6 } } for cat, cat_data in cats.items(): c = add_cat(cat,cat_data["views" ],cat_data["likes" ]) for p in cat_data["pages" ]: add_page(c, p["title" ], p["url" ]) for c in Category.objects.all (): for p in Page.objects.filter (category=c): print ("- {0} - {1}" .format (str (c), str (p))) def add_page (cat, title, url, views=0 ): p = Page.objects.get_or_create(category=cat, title=title)[0 ] p.url=url p.views=views p.save() return p def add_cat (name,views,likes ): c = Category.objects.get_or_create(name=name,views=views,likes=likes)[0 ] c.save() return c if __name__ == '__main__' : print ("Starting Rango population script..." ) populate()
利用Model.object.get_or_create()函数,通过循环遍历add_cat,add_page创建,通过save函数导入数据库,在之后我们通过调用模型来调用其背后数据库中的一个个Category/Page了
前端 html如果没学的可以在网上随便找个教程,学习模式与markdown一样,大概花个一两个小时大概了解一下,边学边敲点例子就可以了,这里我们创建一个主页
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <!DOCTYPE html > <html > <head > <title > Rango</title > </head > <body > <h1 > Rango says...</h1 > <div > hey there partner! <br /> </div > <div > <h2 > Category</h2 > {% if categories %} <ul > {% for category in categories %} <li > <a href ="{% url 'show_category' category.slug %}" > {{ category.name }}</a > </li > {% endfor %} </ul > {% else %} <strong > There are no categories present.</strong > {% endif %} </div > </body > </html >
这里我们前端想呈现出各个Category的名字,把鼠标放在上面还可以跳转至某个地方(也就是该category的域名) 仔细想想,我们有哪些需要解决?
这里{}中间包着的% %是什么?
我们如何获取到category的数据?我们又是从哪里获取到代码中categories的信息的?
我们如何跳转到”url ‘show_category’ category.slug”这个地方,这里具体是指什么,category.slug是作为变量吗?用来干嘛的? 通过回答这几个问题,我们就可以大概理解前端-前后端交互-后端的流程了
第一个问题 实际上,在Django中,Django模板系统的语言嵌入到了html中,感兴趣的可以阅读https://docs.djangoproject.com/zh-hans/5.0/ref/templates/language/
第二个问题 既然是前端-前后端交互-后端,必然要层层递进获取数据,这里我们通过urls.py和views.py来实现前后端交互 @urls.py
1 2 3 4 5 6 7 8 9 10 11 12 from django.urls import pathfrom django.urls import re_path from rango import viewsfrom django.conf import settingsfrom django.conf.urls.static import staticurlpatterns = [ path('' , views.index, name='index' ), ] if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
@views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 from django.shortcuts import renderfrom django.http import HttpResponsefrom rango.models import Categoryfrom rango.models import Page def index (request ): category_list = Category.objects.order_by('-likes' )[:5 ] pages_list = Page.objects.order_by('-views' )[:5 ] context_dict = {'categories' : category_list, 'most_liked_category' :category_list.first(), 'most_viewd_pages' :pages_list} return render(request, 'rango/index.html' , context_dict) def about (request ): print (request.method) print (request.user) return render(request, "rango/about.html" ,{}) def show_category (request, category_name_slug ): context_dict = {} try : category = Category.objects.get(slug=category_name_slug) pages = Page.objects.filter (category=category) context_dict['pages' ] = pages context_dict['category' ] = category except Category.DoesNotExist: context_dict['category' ] = None context_dict['pages' ] = None return render(request, 'rango/category.html' , context_dict)
@templates 我们需要在项目目录中建一个template文件夹并配置好它 settings.py 文件的顶部有个名为 BASE_DIR 的变量,它的值是 settings.py 文件所在目录的路径。这 里用到了 Python 的特殊属性 __file__,它的值是所在文件的绝对路径。调用 os.path.dirname() 的作用是获取 settings.py 文件所在目录的绝对路径,再调用一次 os.path.dirname() 又去掉一层, 因此 BASE_DIR 最终的值是 /tango_with_django_project/。如果你还不太理解这个过 程,可以把下面几行代码放到 settings.py 文件中: print(file ) print(os.path.dirname(file )) print(os.path.dirname(os.path.dirname(file ))) 有了 BASE_DIR 之后,我们便可以轻易引用 Django 项目中的文件和目录。我们可以定义一个名为 TEMPLATE_DIR 的变量,指向 templates 目录的位置。这里还要使用 os.path.join() 函数拼接多个 路径片段。TEMPLATE_DIR 变量的定义如下: TEMPLATE_DIR = os.path.join(BASE_DIR, ‘templates’) 我们使用 os.path.join() 函数把 BASE_DIR 变量和 ‘templates’ 字符串拼接起来,得到/tango_with_django_project/templates/。如此一来,我们便可以使用 TEMPLATE_DIR 变量替代前面在 TEMPLATES 中硬编码的路径。把 DIRS 键值对改成下面这样: ‘DIRS’: [TEMPLATE_DIR, ] @templates/index.html
所以,我们根据个人爱好创立并编写index.html和about.html以及category.html 当html中存在变量时,通过__views__.py中的对应函数提供上下文字典,而上下文字典从数据库中的模型中调取,这样我们便实现了前后端的数据传递
第三个问题 在 Django 的模板中,% url ‘show_category’ category.slug % 是一个 URL 模板标签,它会生成一个 URL。这个 URL 是通过查找名为 ‘show_category’ 的 URL 模式并将 ‘category.slug’ 作为参数来生成的。
show_category 是在你的 urls.py 文件中定义的 URL 名称,它对应一个视图函数,这个视图函数负责处理请求并返回一个响应。当用户点击这个链接时,浏览器会向这个 URL 发送一个 GET 请求,然后 Django 会调用与这个 URL 关联的视图函数。
category.slug 是一个变量,它是 Category 对象的一个属性。在创建models时,初始函数会根据models的name来生成它的slug并保存。在这个上下文中,category 是一个 Category 对象,slug 是这个对象的一个字段,它通常包含一个用于 URL 的简短标签。在这个例子中,category.slug 的值会被插入到生成的 URL 中。
所以, url ‘show_category’ category.slug 、 这个模板标签会生成一个链接到特定分类页面的 URL,当用户点击这个链接时,他们会被带到这个分类的页面。 具体定义如下 @rango/models.py/class category
1 2 3 4 5 6 7 8 9 10 11 12 13 class Category (models.Model): name=models.CharField(max_length=128 ,unique=True ) views=models.IntegerField(default=0 ) likes=models.IntegerField(default=0 ) slug = models.SlugField(default='' ,blank=True ) def save (self, *args, **kwargs ): self.slug = slugify(self.name) super (Category, self).save(*args, **kwargs) class Meta : verbose_name_plural='Categories' def __str__ (self ): return self.name
@rango.urls.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from django.urls import pathfrom django.urls import re_path from rango import viewsfrom django.conf import settingsfrom django.conf.urls.static import staticurlpatterns = [ path('' , views.index, name='index' ), path('about/' , views.about, name='about' ), re_path(r'^category/(?P<category_name_slug>[\w\-]+)/$' , views.show_category, name='show_category' ), ] if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
其中引入了正则表达式的概念,感兴趣的同学可以去了解一下