小编我初学Django的时候跳了不少的坑,曾经一度想放弃。现在想想,幸亏没有哈。坚持学习持续改进才是王道啊,要不然老是中途放弃肯定一事无成。我记忆最为深刻的就是看着官网的入门教程练手,当我在模板里看到for choice in question.choice_set.all时, 心里快崩溃了。Question模型里根本没有choice_set这个字段或方法啊,这是什么鬼。后来得知这是Django进行反向查询的方式。今天小编我来专门总结下Django新手容易犯的常见错误,并教你如何避免choice_set那样的大坑。本文适合初学者,老鸟们请多指教啊。
模型坑
请仔细观察下面Django模型,你能找到几处坑呢? Django项目第一步就是写模型,如果你动笔没写5行就包含了一堆错误,相信你一定会先怀疑自己,然后再怀疑人生。
classPerson(models.Model): name = models.CharField('Name')
classBook(models.Model):
name = models.TextField('Name', blank = True, null = True)
author = models.ForeignKey('Person', related_name='author')
答案是4个错误,你发现了吗? 如果你能找到更多错误,请留言。
Person模型中的CharField没有指定字符串最大长度max_length。CharField和TextField类似,都是字符串,区别在于CharField必需要设置max_length, 而对TextField而言max_length是可选的。这在数据库层面是有用的,设置最大长度可以帮助节省数据库空间。
Book模型中ForeignKey应该是Person,而不是'Person'。加了引号就把Person变成普通的字符串了,变成verbose_name了。
如果你设置了ForeignKey,on_delete删除选项是必需要设置的,比如on_delete=models.CASCADE, on_delete=models.SET_NULL。前者意思是如果一个person删除了,其对应的所有books要删除。后者意思是,如果一个person删除了,其对应的所有books变为NULL。
related_name应该是用来做反向查询的名字,而不是与字段相同的verbose_name。在这里related_name应该改为‘book’或'books'。如果你不设置related_name, 你要从Person反查Book就要使用Person.book_set.filter()或者Person.book_set.all()。如果你设置了related_name, 你就可以使用Person.book了。
当然还有一个最后技术细节也值得关注,那就是Book模型中TextField的blank=True和null=True的区别。其实本例中完全没有必要同时设置blank=True和null=True,原因如下。
blank 是针对表单的,如果 blank=True,表示你的表单填写该字段的时候可以不填,但是对数据库来说,没有任何影响。
null 是针对数据库而言,如果 null=True, 表示数据库的该字段可以为空,即在Null字段显示为yes。
对于CharField和TextField,如果为空字符串或没有字符,数据库里会存储''空字符串,不会以null形式存储,所以设置nul=True没有任何意义。
对于在模型中修改现有字段和新增字段合理地设置blank=True和null=True非常重要,否则当你运行python manage.py makemigrations和python manage.py migrate时, Django会要求你提供默认值,那是相当的烦。
当你编写模型时,请一定了解哪些选项是必需设置的,更多内容见:
视图坑
老实说,视图的坑不多。不过值得注意的是,我们需要了解的是何时使用save_m2m方法只用来存储多对多的关系。当你同时满足下面两个条件时,你必须要使用此方法。如果你直接使用modelform.save()或form_valid()方法,是可以直接存储多对多(m2m)关系的,不需要用save_m2m。
你使用了save(commit=False)方法,添加了额外的自动user
你的model里有多对多的关系(比如tags)
假设我们文章模型里有tags这个多对多的字段,我们还需要在视图里增加一行save_m2m, 否则多对多关系不会被存储。
defarticle_create(request):
ifrequest.method =='POST':
form = ArticleForm(request.POST)
ifform.is_valid():
article = form.save(commit=False)
# commit=False tells Django that "Don't send this to database yet.
article.author = request.user# Set the user object here
article.save()# Now you can send it to DB
form.save_m2m()
returnHttpResponseRedirect("/blog/")
else:
form = ArticleForm()
returnrender(request,'blog/article_create_form.html',{'form': form})
设置文件settings.py坑
在你运行python manage.py migrate前,请一定把它加到settings.py里INSATLLED_APP里去,如下所示,要不然会出现错误。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.staticfiles',
'django.contrib.sites',
'myapp',
]
如果你要使用到静态文件如css和图片,你还需要在settings.py里设置STATIC_URL和MEDIA,否则静态文件无法正确显示。这样用户上传的图片会放在/media/文件夹里。请注意这个设置是通用的哦,新手直接拿去用吧。:)
STATIC_URL ='/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR,"static"),]
# specify media root for user uploaded files,
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL ='/media/'
同时也别忘了把你app的urls.py加到项目里的urls.py里去。显示图片和静态文件,还需在结尾部分加static配置。
fromdjango.conf.urlsimportpath,include
fromdjango.contribimportadmin
fromdjango.confimportsettings
fromdjango.conf.urls.staticimportstatic
urlpatterns = [
path(r'^myapp/',include('myapp.urls')),
path(r'^admin/',admin.site.urls),
] + static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
re_path(r'^restaurant/(?P
\d+)/dishes/(?P
\d+)/$',
views.DishDetail.as_view(),name='dish_detail'),
同样当你使用reverse方法对命名url进行反向解析时,请确保你传递的参数数量与你设计的URL中的参数数量是一致的。
表单坑
上传图片和文件时,模板中form一定加enctype="multipart/form-data“属性, 同时视图中别忘了加request.FILES, 如form =UploadForm(request.POST, request.FILES)。
如果你在forms.py里通过clean方法自定义表单验证,那么视图中请用form.cleaned_data.get('field_name')获取验证过的数据,而不是直接使用request.POST['fileld_name']获取表单提取来的数据, 否则表单不会进行验证,那么你的clean方法也白定义了。小编我曾被这个坑怕了。
另外的你的模板里的form加{% csrf %}了吗?
小结坑
小编我专门总结了Django一些常见错误,希望能帮你避免如坑。坑并不可怕,可怕的是被坑怕了! 聪明小心的你还经历过哪些坑,欢迎评论区留言啊。
大江狗
2018.8.17
下篇我打算写利用Django开发智能文档管理系统的教程,欢迎关注。
领取专属 10元无门槛券
私享最新 技术干货