自定义用户模型
- 暂时先不migrate
- 注意settings:
- INSTALLED_APPS
- AUTH_USER_MODEL
- LOGIN_REDIRECTED_URL, LOGOUT_REDIRECTED_URL
- STATICFILES_DIRS
- model中新建CustomUserModel custom user model基于 abstract user生成
- form中新建UserCreationForm和UserChangeForm 分别代表两种访问user model的方式:新建以及修改(供超级用户)
- 将model和form在admin中注册
- makemigrations, migrate
- 建立superuser
notes
- model的fields和关于一个model的form的fields不一定是一模一样的。比如user model可以包含很多内容,但是注册form只需要username,email和password就够了
- 这里继承的UserCreationForm,UserChangeForm都是继承于forms.ModelForm。ModelForm要求关于fields和model的各种设置放在Meta subclass里面。这样可以避免名称冲突。参见Stack Overflow以及document
- 而CustomUserAdmin,继承UserAdmin(这个则继承于admin.ModelAdmin),不用通过Meta设置。在CustomUserAdmin中,目前先针对add_form,form还有model做一个设置
- 在makemigrations以及migrate以后,仍旧还能够对CustomUser添加fields,这个是可以的
用户登录
之前的用户账号管理部分也对登录、注册的机制做了一些常识,这里是在完成了custom user model以后,更全面的一个编码
templates
- 主要的template都在registration目录
- 大部分工作都是在写templates
- templates的放置:
- login.html等等在auth.urls.urlpatterns中提及的views对应的template都放在registration中
- base.html, home.html放在templates目录,而signup.html和之前的情况一样,也是放在templates目录中(不管有没有建立accounts app)
urls
- 在project级别的urlpatterns里面,users先指向自定义的users.urls,然后才是django自带的
- users.urls所指向的是SignUpView
views
- SignUpView 继承 CreateView,作为注册这一用途,常规需要提供form_class, success_url以及template_name。
- blogs app中也继承过 CreateView,但是当时是设置了model 和 fields
forms
使用原先的UserCreationForm.Meta.fields + (...)的办法只能覆盖少数的几个field,包括username和password,但是我们还需要email,可能也需要first_name和last_name(这些可以在abstractUser的model定义里看到),所以直接使用一个tuple列举各个fieldname可能还更好
bootstrap
前续工作收尾
- 首先新建一个pages app替换掉之前home指向的TemplateView, project层面,修改settings和urls
- 在pages app中建立urls, 修改views
- test: django自带的功能是不需要测试的,自建的功能就需要测试。书中以template页面作为单位考虑测试,需要测试的页面包括home和signup两个页面
- 从url出发, hard coded url和以name reverse得出的url两方面都要测试, template used也应该测试
note: hard coded url末尾需要加上
/
, 否则肯定出错的
Bootstrap
bootstrap建议使用cdn在线读取, 国内也有资源. 应该载入的资源包括:
• Bootstrap.css • jQuery.js • Popper.js • Bootstrap.js
主要的修改都集中在base.html里面. 添加了一个navbar. 接下来要对signup form作一些修正. 书里面使用的是第三方库 django-crispy-forms
使用步骤:
- 安装django-crispy-forms (via. pip)
- 在settings中将
'crispy_forms',
加入installed apps, 同时由于我们使用了bootstrap4而不是默认的bootstrap2, 需要添加CRISPY_TEMPLATE_PACK = "bootstrap4"
- 在template文件中, 顶端加入:
{% load crispy_forms_tags %}
, 原先的{{form.as_p}}
修正成{{form|crispy}}
就可以了.
crispy form还有更丰富的功能, 不过目前暂时不作深入学习
密码修改与重设
这个是auth.urls的urlpatterns中剩下的几个东西. 实际上django已经有修改密码以及密码重设的模板了, 现在需要做的是自己建相应的模板. 这些模板文件都放在registration文件夹中
密码修改
password change, password change done这两个页面不需要做太多 顺带一提,password_change不能直接通过users/password_change进入,因为这个是给已登录用户才开发的。
password reset这一块需要额外设置如何发送邮件. sendgrid是一个可行的工具,但是国内的适用性还有待论证。实际上sendcloud应该是更好用的,人家一天限量10封,基本上是应该够用的了(开发阶段)。生产阶段的话肯定就要花钱去订阅了……或者手工?毕竟大家都喜欢把密码设置成123456……
密码重置
重设这里,我发现走完步骤以后,点击登录的时候会报错,这是因为这时候系统会跳转到accounts/login,但是accouts这一系列的url在project级别没有注册过,所以要添加进来,当然可以继续用django.contrib.auth.urls
来做
模板
- password_rest_form.html
- password_rest_done.html
- password_rest_confirm.html
- password_rest_complete.html
email和sendcloud/sendgrid
关于邮件这一块先放一下, 等到网络环境较好的时候才开始做. 邮件这一章涉及了源码>>参考>>自建这样一个工作流程. 对于django这种非常庞大的项目, 通读全部代码是很困难的, 但是根据实际需求阅读相应的代码还是比较可行的
app实际内容的编写
articles app 和之前的blogs相似。编写的过程肯定也是:
- models
- template, views, urls
- urls包括project level的include,以及app level的urlpatterns
- views 目前包括一个ListView
- template中:
- object_list, 当然也可以通过context_object_name (views.py) 设置
- 用的是bootstrap的card这个对象。建议进一步阅读。
- 行内文本分区使用span
- 不同区域(题目,正文以及辅助区域)的分区使用的是div
另外,crispy forms如果只是用filter恐怕还是不能很好的排版,看来还是要学习form helper才行
添加edit,delete与list功能
- urls:依靠
<int:pk>/.../
设置url。 - views方面,注意ListView和DetailView来自 generic, 而DeleteView以及UpdateView来自 generic.edit。 todo:能否从generic导入DeleteView与UpdateView: 经过测试, 直接从generic也是可以导入的, 所以到底有什么区别呢? 包括model的继承, 有人写models.Model, 但是models好像也可以?
- 其实想一下, 各种view的写法真的差不多, 但是为什么可以实现各自的功能呢? 这不全靠着各种generic view么?
认证与鉴权
create view 的修改
- author应该是当前用户
在书中这个功能是借助form_valid()函数做的。但是具体原理书中语焉不详。这一块肯定要看一下django 文档中关于form的部分,另外最好也把crispy form一起看了。
- 鉴权
class based views需要class mixin来提供鉴权功能
LoginRequiredMixin : 只有登录用户才能进入的view UserPassesTestMixin : 对用户权限进一步细分. test指的是test_func, 需要手动override
def test_func(self):
obj = self.get_object()
return obj.author == self.request.user
test_func中使用的get_object:
get_object(queryset=None) Returns the single object that this view will display. If queryset is provided, that queryset will be used as the source of objects; otherwise, get_queryset() will be used. get_object() looks for a pk_url_kwarg argument in the arguments to the view; if this argument is found, this method performs a primary-key based lookup using that value. If this argument is not found, it looks for a slug_url_kwarg argument, and performs a slug lookup using the slug_field.
When query_pk_and_slug is True, get_object() will perform its lookup using both the primary key and the slug.
注意 self.request, 而且request里面还有user?
request.user除了可以在view里面使用, template里面也是可以的
至于书中说的编辑/删除在非当前用户写作文章记录里不予显示, 这个功能用不上custom template tag, 使用 {% if request.user == article.author %}就可以了. custom template tag确实有用, 但是不是用在这个功能上面
顺便说一下书里面也没有给article写test. 不过等我们跟着comment一章走完以后其实这一个学习project就算走完了, 接下来应该开始着手做会议系统了.
comments
这里主要是一个inline form
虽然书中说新建一个app是over engineering, 不过我们还是尝试一下.
custom user那里的admin class:
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['email','username','age','is_staff','desc']
如果跟踪UserAdmin看过去可以发现, 这个class继承的是admin.ModelAdmin, 所以对于article和comment, 都可以自建一个admin class, 调节list_display, 而具体到编辑页面, 还能够添加inline编辑功能:
class CommentInline(admin.TabularInline):
model = Comment
extra = 1
class ArticleAdmin(admin.ModelAdmin):
model = Article
list_display = ['title','date','author']
inlines = [
CommentInline
]
admin.site.register(Article, ArticleAdmin)
注意上面实现步骤, 首先建立一个inline Class, 继承于admin的TabularInline/ StackedInline. 然后在ArticleAdmin里面的inlines field将其纳入
至于对inline class 的微调(额外显示的条目数), 自然是在inline class的内部
template方面
这里第一次遇到反向查找. 之前我们是从一个object通过foreignkey找到它的宗主, 比如从article找到author. 但是现在我们是从article找到comments, article在这里是comment的宗主, 对于这种反向查找可以使用query_set
query_set在具体使用时是foo_set, 而foo则是对面model的名称的小写, 所以这里应该是comment_set. 和context_object_name一样, 这里也可以手动设置一个容易理解的名称作为query_set使用.
这个书的学习记录就到此为止了