这个文章主要是记录我在学习django过程中所遇到的问题,为后续其它个人项目做铺垫,之前陆陆续续的在使用djano,但是一直都没有好好的去记录一些内容,借此机会完整的记录下学习过程遇到的问题和注意点。django最适合用于做网站,从django官网上的slogan看的出来了。至于django有多适合进行网站开发估计得要接着后续的学习才能知道了,不过我用django主要还是用与做api服务,在考虑之中的还有flask,flask比django更加简单,其中这个repo是基于flask做的api服务。
django-admin startproject <项目名称>
python包文件必须包含的,只要有这个文件,说明该Python文件目录下的所有文件都是一个包。
python manage.py runserver
python manager.py runserver 0.0.0.0:8000
并且需要把域名在setting.py中的ALLOWED_HOSTS
字段中。
忘记命令manage.py
提供的命令,可以采用python manage.py help
进行查看。
可以通过python manage.py createsuperuser
创建创建超级管理员
django-admin startproject <项目名称>
制造迁移(迁移数据库可以只保存这个)python manage.py makemigrations
迁移python manage.py migrate
写url时最好是通过path方式,而不是用正则走re_path/url
的方式,因为会更加直观一些。
eg:
try:
article = Artcle.objects.get(id=article_id)
except Artcle.DoesNotExist:
return HttpResponse("不存在”)
前端页面和后端代码进行分离,降低耦合性。django中规定了模板文件(html)的存放位置和格式,在project下的setting.py文件中的TEMPLATES
可以看到且修改,如果采用默认配置,需要在app中新建templates
文件夹,在其中写好对应的模板文件,
eg:这是循环打印文章列表的模板例子,
<html>
<head>
</head>
<body>
{% for article in articles %}
<a href="{% url 'article_details' article.pk %}">{{ article.title }}</a>
{% endfor %}
<h2>{{article_obj.title}}</h2>
<p>{{article_obj.content}}</p>
</body>
</html>
使用django.shortcuts
app中的render/render_to_response/get_objects_or_404
都可以进行模板渲染和调起。
这是一种返回数据的写法:
def article_details(request, article_id):
try:
article = Artcle.objects.get(id=article_id)
context = {}
context['article_obj'] = article
return render(request, "article_details.html", context)
except Artcle.DoesNotExist:
raise Http404("not exist")
return HttpResponse("文章标题:%s
文章内容:%s" % (article.title, article.content))
这是另外一种,使用from django.shortcuts import get_object_or_404
def article_details(request, article_id):
article = get_object_or_404(Artcle, pk=article_id)
context = {}
context['article_obj'] = article
return render(request, "article_details.html", context)
这样做会简洁很多,这也是django所推崇的做法。
列出所有文章。模板中可以采用{/article/{{ article.pk }}
或者{% url ‘article_list’ article.pk %}
,用第二种方法需要在对应的urls.py
文件中添加对应的path.name。
很多时候我们不应该把所有的路由设置都放在project下的urls.py
文件中,最佳的做法应该是把对应的url放在各自的appurls.py
文件中(需新建),做法如下所示:
# project `urls.py`
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('article/', include('artcle.urls'))
]
# article `urls.py`
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.article_list, name="article_list"),
path('<int:article_id>', views.article_details, name="article_details"),
]
千万要注意对应文件的路径!!!
通过使用定制类中的__str__给用户定制显示内容,当然python中的定制类不止只有这些,详情可以参考廖雪峰
def __str__(self):
return "Article: %s" % self.title
想要在admin管理页面中显示字段,可以在对应app的admin.py
中这么做:
class ArticleAdmin(admin.ModelAdmin):
list_display = ("title", "content”)
# admin注册(其中的一种方法)
admin.site.register(Artcle, ArticleAdmin)
class ArticleAdmin(admin.ModelAdmin):
list_display = ("id", "title", "content")
ordering = ("id", )
ordering默认是升序,改为-id,则为降序排序
from django.utils import timezone
,第一第二种需要这个库。
- 如果是直接写。
created_time = models.DateTimeField()
并在对应app中的models.py文件中添加进了新字段,运行时会告知模型和数据库匹配不上,需要去同步数据库,当同步数据库的时候,系统添加默认值,两种方法,第一种在terminal中直接添加,第二种退出然后再添加默认值。 - 同上第二种,退出后再添加默认值。
created_time = models.DateTimeField(default=timezone.now)
created_time = models.DateTimeField(auto_now_add=True)
last_updated_time = models.DateTimeField(auto_now=True)
在project的setting.py
文件中找到TIME_ZONE
字段,TIME_ZONE = 'Asia/Shanghai’
(不知道为啥没有北京时间
导入from django.contrib.auth.models import User
。给每篇文章添加作者信息,外键。 author = models.ForeignKey(User, on_delete=models.DO_NOTHING, default=1)
最好不要真的删除,而是作为用户不可见。文章模型添加字段is_deleted = models.BooleanField(default=False)
。文章views.py
的添加数据筛选
def article_list(request):
articles = Artcle.objects.filter(is_deleted=False)
context = {}
context['articles'] = articles
return render_to_response("article_list.html", context)
pip install virtualenv
下载过程中若出现如下信息:
Traceback (most recent call last):
File "/usr/bin/pip3", line 11, in <module>
sys.exit(main())
File "/usr/lib/python3/dist-packages/pip/__init__.py", line 215, in main
locale.setlocale(locale.LC_ALL, '')
File "/usr/lib/python3.5/locale.py", line 594, in setlocale
return _setlocale(category, locale)
locale.Error: unsupported locale setting
则是因为语言配置所导致的,执行如下命令即可:
$ export LC_ALL=C
目的是为了去除所有本地化的设置,让命令能够正确执行。LC_ALL
它是一个宏,如果该值设置了,则该值会覆盖所有LC_*的设置值。注意,LANG的值不受该宏影响。C
是系统默认的locale,"POSIX"是"C"的别名。所以当我们新安装完一个系统时,默认的locale就是C或POSIX。
$ cd env
$ source ./bin/activate
<p>{{ blog.content|truncatechars:30 }}</p>
,也可以使用truncatewords
,但是要求词和词中间要有空格,针对的是一个个的词,英文可以直接用。
模板文件(一般都是html)如果是跟着项目走,那就应该放到全局模板文件里,如果是跟着app走应该放到app的模板文件里。
在app中新建的static文件夹中再新建一个跟app名字一样的文件夹,然后把需要的静态文件放进行。注意:要重启服务器(CSS没效果也可以重启)
{% load staticfiles %}要放在需要用到的静态文件之前,而不是拆开。
{% load staticfiles %}
{% block header_extends %}
<link rel="stylesheet" href="{% static 'blog/blog.css' %}">
{% endblock %}
bootstrap和jquery最好都down下来,毕竟也不是特别大。使用bootstrap推荐直接上官网查资料。www.bootcss.com
<p>{{ blog.content|striptags|truncatechars:120 }}</p>。
<div class="blog-content">{{ blog.content|safe }}</div>
Django
中实现事物主要有两种方式:一是通过 Django ORM
框架的事物处理;另外一种是基于原生执行 SQL
语句的 transaction
处理。
至于为什么需要做事物管理,简单来说就是需要对�用户提交的本次操作做完整性保证,有可能用户提交的本次操作涉及10多条 SQL
语句,但如果执行到其中第七第八条时出现问题后,之前执行的却已经被写入数据库中了,按道理说,如果中途出现错误,应该把之前的以及执行完的语句全都撤回。具体细节可参考我的另一篇文章
据官方文档中所描述的内容https://docs.djangoproject.com/zh-hans/2.0/topics/db/transactions/ ,Django
中的默认事物级别为 auto-commit
,用文档中举的例子来说,当我们在 Django
中做的 model().save()
和 model().delete
操作所有的改动都会立即提交,没有 rollback
。
- 将
http request
数据库操作全都包括。 这种做法相当的简单粗暴,具体配置如下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '',
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
'ATOMIC_REQUESTS': True, // !!!
}
}
这种方法是在 django
调用每个视图方法前都启动一个事物,并且要保证该响应没有任何问题, django
最终才会提交这个事物;如果在这其中出现了其它问题, django
会自动的回滚该事物。 这样会非常的简单,但不适合流量大的服务,因为这要对每个视图函数都要开启事物,而且回滚的只是数据库操作,如果本次操作中途涉及到了一些其它操作,比如执行了到了一半发送了邮件,但是下一步操作后发现有错误要回滚,但邮件此时已经发送出去了,没法回滚除了数据库之外的操作。
-
中间件拦截。 这个办法是我最开始采用的,但无奈一直
Run
不起来,需要添加上这个中间件django.middleware.transaction.TransactionMiddleware
。我在 django 官方文档中一直没找到这个中间件,估计在 2.0 被丢弃了吧。 -
手动通过装饰器进行管理。 这是官方文档中描述最清楚的内容,也是最方便的内容,具体的细节可以直接去看文档。因为现在项目还有很多变化的地方,等后续业务逻辑稳定了再使用该方法。
使用这种方法基本上就是存粹的手写 SQL
语句了,如果我们需要做更多细致的东西可以直接采用这种做法。通过连接定义一个游标 cursor
,通过 cursor
执行sql语句,最后通过 transaction.commit_unless_managed()
来提交事务。
需要注意的地方是,使用原生 SQL 处理事物和使用 django ORM 框架进行处理的区别在于,如果视图函数中出现的问题是视图函数本身而不是数据库的问题,那么使用 django ORM 框架也会回滚,而使用原生 SQL 处理却不会。
写完 model 后,需要把对应的 app 放到 setting 配置文件中。
很多时候当我们在修改 django 的 model 结构时,会因为某些“特殊”情况(搞不懂为啥)没有更新数据库表结构,这个时候可以参考如下做法:
- 把数据库中对应的表删除;
- 把
django_migrations
表中app
对应字段中的 model 删除; - 重新执行
makemigrations
和migrate
。
按照正常的 django 流程做图片(或其它资源文件)上传即可,如最终生成的 json 格式如下:
{
avatar = "/media/avatar/pjhubs.jpg";
"masuser_id" = 7028492784;
}
此时在浏览器中直接访问 http://hostname/media/avatar/pjhubs.jpg
,是访问不到资源文件的,需要这么做:
- 在 project app 下(与 settings.py 同级别)的 url.py 文件中添加:
from . import settings
from django.conf.urls.static import static
- 在末尾添加上:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
可根据自己的要去进行设置,此时就可通过 http://hostname/media/avatar/pjhubs.jpg
访问到资源文件了。
Django 的 request.POST
方法获取到的 POST 方法参数只支持 Content-Type
类型为:
multipart/form-dat
application/x-www-form-urlencoded
其它类型的 Content-Type
通过 request.POST
方法获取到的参数列表均为空。但一般来说我们都希望在 POST 请求中参数类型为 json
,所以我们需要让客户端同学把 POST 请求的 Content-Type
设置为 application/json
类型。
在 iOS 的一个常用网络请求框架 AFNetworking
中,默认的 POST
请求 Content-type
就是为 application/json
。
使用 Q
对象,不可使用 filter
,因为 Django 默认为 AND
关系。
注意:
- 如果涉及到多参数时,
Q
对象应该在前,其它参数在后。
原本是想基于 ES
来一套搜索全家桶的,但无奈 ES
太重了,基于 django-haystack
最后完成了需求,相关配置如下:
- 下载相关依赖
pip install django-haystack whoosh
- 创建相关文件
-
settings.py
添加app
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'haystack', # 在所有自定义 app 之上 ]
# 配置全文搜索 # 指定搜索引擎 HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', 'PATH': os.path.join(BASE_DIR, 'whoosh_index'), }, } # 指定如何对搜索结果分页,这里设置为每 10 项结果为一页,默认是 20 项为一页 HAYSTACK_SEARCH_RESULTS_PER_PAGE = 20 # 添加此项,当数据库改变时,会自动更新索引 HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
注意: 需要给
settings.py
中设置好templates
文件夹目录TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 重点 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
-
tempaltes
文件夹。估计要新建,最终创建出的目录层级为:├── templates └── search └── indexes └── user └── xxx_text.txt
xxx 即为需要创建搜索索引的
app
名称,若有大小写,则全小写即可。该txt
文件写下需要进行被索引的字段即可,objct
即为xxx
的传入对象实体,不需要修改。{{ object.nick_name }}
-
models.py
需要做索引app
。在app
的目录下创建新文件search_indexes.py
,作为索引类from .models import MasUser from haystack import indexes class MasUserIndex(indexes.SearchIndex, indexes.Indexable): text = indexes.CharField(document=True, use_template=True) # 需要搜索的字段 nick_name = indexes.CharField(model_attr='nick_name') def get_model(self): return MasUser def index_queryset(self, using=None): return self.get_model().objects.all()
-
views.py
中的使用。@decorator.request_methon('GET') @decorator.request_check_args(['s_nick_name']) def searchFriend(request): nick_name = request.GET.get('s_nick_name') users = SearchQuerySet().models(MasUser).filter(nick_name__contains=nick_name) f_users = [] for user in users: f_users.append(user.object.toJSON()) json = { 'users': f_users } return utils.SuccessResponse(json, request)
-
建立索引
python manage.py rebuild_index
-
最后自己配置一下路由即可。
sudo apt-get install python3.6-dev
使用 python manage.py makemigrations
命令来触发数据库表的生成和更新,需要保证在 app 目录下有 migrations
这个包。注意,这里说的是包!包!包!!!
from decimal import Decimal
current_drink_score.score = Decimal(str(10))
- 查找到自己的 ip 地址。
- 在 django 的
ALLOWED_HOSTS
中添加上该 ip 。 - 通过
python runserver ip:port
允许项目即可。
或者直接修改 pyCharm 中的 web 服务器工程配置:
from django.db.models import Q
myapps = App.objects.filter(~Q(name= ''))
导致执行 migrate
时各个 app 的上下游依赖出现问题,报错:
django.db.migrations.exceptions.InconsistentMigrationHistory: Migration user_avatar.0001_initial is applied before its dependency user.0002_auto_20190705_2356 on database 'default'.
这是因为在 django_migrations
表中有可能因为 user_avatar.0001_initial
已经有记录了,且依赖 user.0002_auto_20190705_2356
,但此时 user.0002_auto_20190705_2356
并未生成。
解决办法:把 user_avatar.0001_initial
这条记录删掉,但非常不好。
如果是直接 copy 了别人的 django 文件夹,可能会出现它的 env 与本机的各种不匹配问题,需要删除重新生成