Python-Django
准备工作
新建一个Django项目
# 新建一个django项目 $ django-admin startproject mysite # 新建一个app $ django-admin startapp blog
项目的结构
├── blog │ ├── admin.py │ ├── apps.py │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── manage.py └── mysite ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py
# mysite/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', 'markdown2' ]
$ python3 manage.py runserver $ python manage.py collectstatic
一般在urls.py中配置url,在models.py中配置model,在views.py中配置View。
urls.py
Function views
1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
# blog/urls.py from django.conf.urls import url from blog import views urlpatterns = [ url(r'^blog/$', views.IndexView.as_view(), name='index'), url(r'^blog/article/(?P<article_id>\d+)$', views.ArticleDetailView.as_view(), name='detail'), url(r'^blog/category/(?P<cate_id>\d+)$', views.CategoryView.as_view(), name='category'), url(r'^blog/tag/(?P<tag_id>\d+)$', views.TagView.as_view(), name='tag'), ]
使用(?P<>d+)的形式捕获值给<>中得参数,比如(?P
# mysite/urls.py from django.conf.urls import url, include from django.contrib import admin from blog import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'', include('blog.urls', namespace='blog', app_name='blog')) ]
其中namespace参数为我们指定了命名空间,这说明这个urls.py中的url是blog app下的,这样即使不同的app下有相同url也不会冲突了。
假设用户要访问某篇文章,它会自动解析 blog:detail 这个视图函数对应的 url,并且把 article.pk(文章的主键)传递给detail视图函数,details
就是我们在blog/urls.py
中指定的name
。
<a href="{% url 'blog:detail' article.pk %}">{{ article.title }}</a>
如果要访问某个目录
<a href="{% url 'blog:category' category.pk %}">{{ category.name }}</a>
models.py
django.db.models
是orm框架的基础,在blog/models.py
中新建Article
, Category
, Tag
三个model。
class Article(models.Model): STATUS_CHOICES = ( ('d', 'Draft'), ('p', 'Published'), ) # 仍然使用默认的 objects 作为 manager 的名字 objects = ArticleManager() title = models.CharField('标题', max_length=70) body = models.TextField('正文') created_time = models.DateTimeField('创建时间', auto_now_add=True) last_modified_time = models.DateTimeField('修改时间', auto_now=True) status = models.CharField('文章状态', max_length=1, choices=STATUS_CHOICES) # blank和null要同时设置为null,详情参考官方文档 abstract = models.CharField('摘要', max_length=54, blank=True, null=True, help_text="可选,如若为空将摘取正文的前54个字符") views = models.PositiveIntegerField('浏览量', default=0) likes = models.PositiveIntegerField('点赞数', default=0) topped = models.BooleanField('置顶', default=False) category = models.ForeignKey('Category', verbose_name='分类', null=True, on_delete=models.SET_NULL) tags = models.ManyToManyField('Tag', verbose_name='标签集合', blank=True) def __str__(self): return self.title class Meta: ordering = ['-last_modified_time'] # 新增 get_absolute_url 方法 def get_absolute_url(self): # 这里 reverse 解析 blog:detail 视图函数对应的 url return reverse('blog:detail', kwargs={'article_id': self.pk})
Django给我们提供了很多有用的字段,比如上面提到的CharFiled
, TestField
, DateTimeFiled
等等,详情请参考官方文档。
Django中的一对多是在一中进行设置,这里对应于文章的分类,ForeignKey即数据库中的外键。on_delete=models.SET_NULL表示删除某个分类(category)后该分类下所有的Article的外键设为null(空),所以我们同时设置了null=True。多对多就不同,两边都要进行配置。详情请参考官方文档。
class Category(models.Model): name = models.CharField('类名', max_length=20) created_time = models.DateTimeField('创建时间', auto_now_add=True) last_modified_time = models.DateTimeField('修改时间', auto_now=True) def __str__(self): return self.name
class Tag(models.Model): name = models.CharField('标签名', max_length=20) created_time = models.DateTimeField('创建时间', auto_now_add=True) last_modified_time = models.DateTimeField('修改时间', auto_now=True) def __str__(self): return self.name
评论功能的实现
class BlogComment(models.Model): user_name = models.CharField('评论者名字', max_length=100) user_email = models.EmailField('评论者邮箱', max_length=255) body = models.TextField('评论内容') created_time = models.DateTimeField('评论发表时间', auto_now_add=True) article = models.ForeignKey('Article', verbose_name='评论所属文章', on_delete=models.CASCADE) def __str__(self): return self.body[:20]
class ArticleManage(models.Manager): """ 继承自默认的 Manager ,为其添加一个自定义的 archive 方法 """ def archive(self): date_list = Article.objects.datetimes('created_time', 'month', order='DESC') # 获取到降序排列的精确到月份且已去重的文章发表时间列表 # 并把列表转为一个字典,字典的键为年份,值为该年份下对应的月份列表 date_dict = defaultdict(list) for d in date_list: date_dict[d.year].append(d.month) # 模板不支持defaultdict,因此我们把它转换成一个二级列表,由于字典转换后无序,因此重新降序排序 return sorted(date_dict.items(), reverse=True)
我们首先要在project_name/settings.py
中配置好相应的配置文件
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'DB_NAME', 'USER': 'DB_USER', 'PASSWORD': 'DB_PASSWORD', 'HOST': 'localhost', # Or an IP Address that your DB is hosted on 'PORT': '3306', } }
定义完毕后,我们执行下面的命令就在数据库中可以生成相应的数据表:
$ python manage.py makemigrations $ python manage.py migrate
admins.py
参考Mozila的教程以及结合官方文档。
views.py
下面要使用markdown2,所以在INSTALLED_APP里面要添加markdown2,不过这个mardown解析非常的不好,并且弄完还要去下载相应的markdown的css文件,有个专门的网站。
from blog.models import Article, Tag, Category from django.views.generic import ListView, DetailView import markdown2 class IndexView(ListView): # template_name属性用于指定使用哪个模板进行渲染 template_name = "blog/index.html" # context_object_name属性用于给上下文变量取名(在模板中使用该名字) context_object_name = "article_list" def get_queryset(self): article_list = Article.objects.filter(status='p') for article in article_list: article.body = markdown2.markdown(article.body, ) return article_list def get_context_data(self, **kwargs): kwargs['category_list'] = Category.objects.all().order_by('name') # 调用 archive 方法,把获取的时间列表插入到 context 上下文中以便在模板中渲染 kwargs['date_archive'] = Article.objects.archive() kwargs['tag_list'] = Tag.objects.all().order_by('name') return super(IndexView, self).get_context_data(**kwargs)
上面因为我们要进行markdown处理,所以重新自定义了get_queryset
,如果不要进行相应的处理,直接制定model
就行了,get_context_data
可以添加一些额外的字段,比如以后我们要在首页的侧边栏显示目录和标签,所以这里要添加一个category_list
和tag_list
。
class ArticleDetailView(DetailView): model = Article template_name = "blog/detail.html" context_object_name = "article" # pk_url_kwarg会自动和model中相应的主键对应,aritlce_id就是下面配置的URLCONF pk_url_kwarg = 'article_id' # 为了让文章以markdown形式展现,我们重写get_object()方法 def get_object(self): obj = super(ArticleDetailView, self).get_object() obj.body = markdown2.markdown(obj.body) return obj # 新增 form 到 context def get_context_data(self, **kwargs): kwargs['comment_list'] = self.object.blogcomment_set.all() kwargs['form'] = BlogCommentForm() return super(ArticleDetailView, self).get_context_data(**kwargs)
class CategoryView(ListView): template_name = "blog/index.html" context_object_name = "article_list" def get_queryset(self): # url里的cate_id传递给CategoryView,传递的参数在kwargs属性中获取 article_list = Article.objects.filter(category=self.kwargs['cate_id'],status='p') for article in article_list: article.body = markdown2.markdown(article.body, ) return article_list def get_context_data(self, **kwargs): # 增加一个category_list,用于在页面显示所有分类,按照名字排序 kwargs['category_list'] = Category.objects.all().order_by('name') return super(CategoryView, self).get_context_data(**kwargs)
class TagView(ListView): template_name = "blog/index.html" context_object_name = "article_list" def get_queryset(self): """ 根据指定的标签获取该标签下的全部文章 """ article_list = Article.objects.filter(tags=self.kwargs['tag_id'], status='p') for article in article_list: article.body = markdown2.markdown(article.body, extras=['fenced-code-blocks'], ) return article_list def get_context_data(self, **kwargs): kwargs['tag_list'] = Tag.objects.all().order_by('name') return super(TagView, self).get_context_data(**kwargs)
from django.views.generic.edit import FormView class CommentPostView(FormView): form_class = BlogCommentForm template_name = 'blog/detail.html' def form_valid(self, form): target_article = get_object_or_404(Article, pk=self.kwargs['article_id']) # 调用ModelForm的save方法保存评论,设置commit=False则先不保存到数据库, # 而是返回生成的comment实例,直到真正调用save方法时才保存到数据库。 comment = form.save(commit=False) # 把评论和文章关联 comment.article = target_article comment.save() # 评论生成成功,重定向到被评论的文章页面,get_absolute_url 请看下面的讲解。 self.success_url = target_article.get_absolute_url() return HttpResponseRedirect(self.success_url) def form_invalid(self, form): target_article = get_object_or_404(Article, pk=self.kwargs['article_id']) # 不保存评论,回到原来提交评论的文章详情页面 return render(self.request, 'blog/detail.html', { 'form': form, 'article': target_article, 'comment_list': target_article.blogcomment_set.all(), })
template
{% for %}
循环标签,{% if %}
判断标签. {{ variable }}
是一些非常常用的标签
在模板文件中我们可以这样使用,views.py中已经指定了context_object_name = "article_list"
,并且已经在get_queryset()
中进行了markdown处理
{% for article in article_list %} {{article.title}}
通常都会设置一个通用的父模板:
{% extends "base_generic.html" %} {% block content %} ... {% endblock %}
好像要这么这么设置:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'blog/templates')] , 'APP_DIRS': True, ... ]
静态文件
由于源代码丢失,具体情况记得不太清晰,静态文件路径要设置好,如果js文件加载异常,可能是加载顺序的问题。
base_generic.html大概就是下面这样的格式:
<!DOCTYPE html> {% load staticfiles %} <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Myblog</title> <link rel="stylesheet" href="{% static 'lib/css/bootstrap.min.css' %}"> <link rel="stylesheet" href="{% static 'blog/css/style.css' %}"> <link rel="stylesheet" href="{% static 'blog/css/pygments/github.css' %}"> </head> ...
下面这样设置貌似有点问题:
# mysite/settings.py STATIC_URL = '/static/' STATICFILES = os.path.join(BASE_DIR, 'blog/static')
具体参考官方文档
部署
使用uwsgi+nginx
/etc/nginx/sites-available/mysite.conf,blog是app名字,static文件放在了下面,建议直接放在mysite下面,template也是一样:
server { listen 80; location /static/ { alias /home/omrsf/mysite/blog/static/; } location / { uwsgi_pass 127.0.0.1:8001; include /etc/nginx/uwsgi_params; } }
uwsgi -i uwsgi.ini
来启动uwsgi进程,结合nohup &
:
[uwsgi] socket = 127.0.0.1:8001 chdir=/home/ormsf/mysite/ wsgi-file = mysite/wsgi.py processes = 2 threads = 4 chmod-socket = 664
改进
目前文章是直接在admin.py中注册model,然后去admin后台发布的,可以做成api接口,做一个在线的编辑器。增加基本的用户认证功能。
零碎知识点
null和blank的区别
null 是针对数据库而言,如果 null=True, 表示数据库的该字段可以为空。
blank 是针对表单的,如果 blank=True,表示你的表单填写该字段的时候可以不填,比如 admin 界面下增加 model 一条记录的时候。直观的看到就是该字段不是粗体。
render与render_response
优先采用render。
get_absolute_url
model有一个get_absolute_url,它可以与reverse结合起来。
更多Python-Django相关文章请关注PHP中文网!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas

Ramai pemaju laman web menghadapi masalah mengintegrasikan perkhidmatan node.js atau python di bawah seni bina lampu: lampu sedia ada (Linux Apache MySQL PHP) Laman web seni bina memerlukan ...

Penyelesaian kepada Isu Kebenaran Semasa Melihat Versi Python di Terminal Linux Apabila anda cuba melihat versi Python di Terminal Linux, masukkan Python ...

Apabila menggunakan crawler scapy, sebab mengapa fail penyimpanan berterusan paip tidak boleh ditulis? Perbincangan Ketika belajar menggunakan Crawler Scapy untuk Crawler Data, anda sering menemui ...

Bermula dengan Python: Lukisan Grafik Hourglass dan Pengesahan Input Artikel ini akan menyelesaikan masalah definisi berubah -ubah yang dihadapi oleh pemula python dalam program lukisan grafik Hourglass. Kod ...

Proses Python Pool mengendalikan permintaan TCP serentak yang menyebabkan pelanggan terjebak. Apabila menggunakan Python untuk pengaturcaraan rangkaian, adalah penting untuk mengendalikan permintaan TCP serentak dengan cekap. …

Sangat meneroka kaedah tontonan python funcools.partial Object in Funcools.Partial Menggunakan Python ...

Pilihan Perpustakaan Pembangunan Aplikasi Desktop Python Python Banyak pemaju Python ingin membangunkan aplikasi desktop yang boleh dijalankan pada kedua-dua sistem Windows dan Linux ...

Ramai pemaju bergantung kepada PYPI (PythonPackageIndex) ...
