文章目錄
1. 配置路由:include
urlpatterns = [
path('admin/', admin.site.urls),
path('article/', include('article.urls', namespace='article')),
path('userprofile/', include('userprofile.urls', namespace='userprofile')),
]
錯誤寫法:path('userprofile/', include('userprofile.urls'), namespace='userprofile'),
,_path() 中多了個參數,namespace 應該在 include 中。
報錯:
File "D:\django_learning\blog_project\blog_project\urls.py", line 22, in <module>
path('userprofile/', include('userprofile.urls'), namespace='userprofile'),
TypeError: _path() got an unexpected keyword argument 'namespace'
2. forms 表單類字段 fields
from django import forms
from django.contrib.auth.models import User
class UserRegisterForm(forms.ModelForm): # 對數據庫進行操作的表單應繼承forms.ModelForm
# 複寫 User 的密碼
password = forms.CharField()
password2 = forms.CharField()
class Meta:
model = User
field = ('username', 'email')
Django項目中某 app 的 forms.py 段代碼如上,執行時報錯如下:
File "D:\django_learning\blog_project\userprofile\urls.py", line 8, in <module>
from userprofile import views
File "D:\django_learning\blog_project\userprofile\views.py", line 5, in <module>
from userprofile.forms import UserLoginForm, UserRegisterForm
File "D:\django_learning\blog_project\userprofile\forms.py", line 17, in <module>
class UserRegisterForm(forms.ModelForm): # 對數據庫進行操作的表單應繼承forms.ModelForm
File "D:\env\lib\site-packages\django\forms\models.py", line 243, in __new__
"needs updating." % name
django.core.exceptions.ImproperlyConfigured: Creating a ModelForm without either the 'fields' attribute or
the 'exclude' attribute is prohibited; form UserRegisterForm needs updating.
在 stackoverflow 上找到有類似的報錯,排在前面的幾個解決方法:
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = '__all__' # Or a list of the fields that you want to include in your form
意思是 需要將 fields 的字段改爲 __all__
,或者在你的 form 中想去 include 的字段,這裏我就是想要 username 和 email 。
或者:使用 exclude = ()
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
exclude = ()
我嘗試了兩種,只有後面這種可以解決,第一種使用 __all__
依舊報錯,什麼原因呢?不明白,最後才發現我寫的 field 是單數,應該是 fields ,好吧,知道了。
3. 拓展 User 後,不刪除原有數據登錄會失敗
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
# 當 userprofile 這個 app 沒有改動 model 時不用遷移數據。
# 用戶拓展信息
class Profile(models.Model):
# 與 User 模型構成一對一的關係
# 每個Profile模型對應唯一的一個User模型,形成了對User的外接擴展
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
phone = models.CharField(max_length=20, blank=True)
avatar = models.ImageField(upload_to='avatar/%Y%m%d/', blank=True)
bio = models.TextField(max_length=500, blank=True)
def __str__(self):
return f'user {self.user.username}'
# 信號接收函數,每當新建 User 實例時自動調用
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
# 信號接收函數,每當更新 User 實例時自動調用
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
每個Profile
模型對應唯一的一個User
模型,形成了對User的外接擴展,因此你可以在Profile
添加任何想要的字段。這種方法的好處是不需要對User
進行任何改動,從而擁有完全自定義的數據表。
遷移好數據後,如果試圖登錄用戶,會得到報錯。這是因爲之前創建的User
數據都沒有對應的Profile
模型,違背了現有的模型。一種解決辦法就是乾脆刪除舊的數據,因此就需要用到Django的shell
命令。
輸入下面兩行指令就可以輕鬆刪除User數據庫:
(env) D:\django_learning\blog_project>python manage.py shell
Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.contrib.auth.models import User
>>> User.objects.all()
<QuerySet [<User: zhuyuping>, <User: taijialan>, <User: zyp>]>
>>> User.objects.all().delete()
(17, {'admin.LogEntry': 9, 'auth.User_groups': 0, 'auth.User_user_permissions': 0, 'article.ArticlePost':5, 'userprofile.Profile': 0, 'auth.User': 3})
因爲前面寫的article
模型中,與User
的外鍵也採用了models.CASCADE
級聯刪除模式,因此隨着User的刪除,相關的文章也一併刪除了。
輸入exit()
退出shell
,輸入指令python manage.py createsuperuser
,重新創建管理員賬戶。
4. Profile.objects.get(user_id=id).exists() 出錯
報錯提示:{AttributeError}‘Profile’ object has no attribute ‘exists’,‘Profile’ object 只有我寫的一些特殊具體的屬性,沒有exists屬性或方法。
通過 dir(Profile.objects.filter(user_id=id))
,可以證明通過 filter 可以產生 exists 屬性。
5. 被包含的子路由模塊需要添加app_name屬性
# blog_project/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('article/', include('article.urls', namespace='article')),
path('userprofile/', include('userprofile.urls', namespace='userprofile')),
path('password-reset/', include('password_reset.urls')),
path('comment/', include('comment.urls', namespace='comment')), # 報錯位置
]
File "D:\django_learning\blog_project\blog_project\urls.py", line 26, in <module>
path('comment/', include('comment.urls', namespace='comment')),
File "D:\env\lib\site-packages\django\urls\conf.py", line 39, in include
'Specifying a namespace in include() without providing an app_name '
django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without providing an app_
name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the
list of patterns and app_name instead.
在新增 comment 評論的模型時,通過 python manage.py startapp comment
新建一個評論的app,然後在 setting 中的 INSTALLED_APPS 列表添加 ‘comment’,在主路由中配置子路由,接着編寫 comment 的模型類,寫完後需要遷移數據庫,執行 python manage.py makemigrations
, 這時候出現上面這個報錯。
報錯信息是指,在 include() 中沒有提供給一個支持的 app_name,需要在被包含的子模塊(這裏是 comment 目錄下的 urls.py 模塊)中設置 app_name 屬性。
具體原因是在 comment 的 urls.py 文件中沒有寫該 app_name = ‘comment’, 以及配置 urlpatterns。
# comment/urls.py
app_name = 'comment'
urlpatterns = []
再次執行遷移:
(env) D:\django_learning\blog_project>python manage.py makemigrations
Migrations for 'comment':
comment\migrations\0001_initial.py
- Create model Comment
6. 使用F12檢查沒有展示的元素
模板中已經寫好前端的展示,視圖函數也給前端傳參了,但是實際就是沒有顯示。
通過F12查看元素,可以發現該標籤中沒有內容,應該是沒有從視圖中獲取到標籤、或者獲取到後經過前端操作後沒有拿到該有的標籤。
自然地,先檢查視圖函數有沒有給前端模板傳遞對象,然後回到該頁面的 html 代碼中檢查,發現是 html 中的變量 articles 寫錯了。
{% for article in articles %}
{% for tag in articles.tags.all %}
<a href="#" class="badge badge-secondary">{{ tag }}</a>
{% endfor %}
7. A server error occurred. Please contact the administrator.
修改 django 文件:D:\env\Lib\site-packages\django\views\debug.py,在打開文件時使用 utf-8,這樣修改後,可以在頁面看到具體的報錯,而不只是一串“A server error occurred. Please contact the administrator.”。
def get_traceback_html(self):
"""Return HTML version of debug 500 HTTP error page."""
with Path(CURRENT_DIR, 'templates', 'technical_500.html').open(encoding='utf-8') as fh:
t = DEBUG_ENGINE.from_string(fh.read())
c = Context(self.get_traceback_data(), use_l10n=False)
return t.render(c)
no such column 報錯:刪庫解決。。。重新生成遷移文件、創建數據。
8. 部署
以前還買了阿里雲服務器,後來就沒有續費了。現在在本地的 Ubuntu 上部署測試。
-
修改 Django 的配置文件
# my_blog/settings.py # 關閉調試模式 DEBUG = False # 允許的服務器 ALLOWED_HOSTS = ['*'] # 靜態文件收集目錄 STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
-
虛擬環境一般是需要在服務器上重新生成的
安裝包:
sudo apt-get update sudo apt-get upgrade sudo apt-get install python3 sudo apt-get install python3-pip sudo apt-get install git sudo pip3 install virtualenv
從遠程庫中拉取項目代碼:
git clone https://gitee.com/zypdominate/django_learning.git
cd 進入項目中,生成虛擬環境,並激活:
virtualenv --python=python3.6 env source env/bin/activate
安裝庫、收集靜態資源、數據遷移了:
pip3 install -r requirements.txt python manage.py collectstatic python3 manage.py migrate
代碼部署基本就完成了,接下來配置
Nginx
安裝 nginx:
sudo apt-get install nginx
啓動 nginx,查看安裝的 nginx 是否正常:
sudo service nginx start
打開瀏覽器,輸入你的服務器公網 IP 地址(可在Ubuntu上試用)查看效果。
接着,重新寫 Nginx 的配置文件。進入
/etc/nginx/sites-available
目錄,這裏是定義 Nginx 可用配置 的地方。輸入指令sudo vi dusaiphoto.com
創建配置文件,以下是已經配置好的:zyp@zyp-virtual-machine:/etc/nginx/sites-available$ ls default my_blog sites zyp@zyp-virtual-machine:/etc/nginx/sites-available$ cat my_blog server { charset utf-8; listen 80; server_name 192.168.171.128; # 暫時是我本地的Ubuntu的ip地址 location /static { root /home/zyp/sites/django_learning/blog_project/collected_static; } location /media { root /home/zyp/sites/django_learning/blog_project/media; } location / { proxy_set_header Host $host; proxy_pass http://unix:/tmp/192.168.171.128.socket; } }
寫的只是 Nginx 的可用配置,所以還需要把這個配置文件鏈接到在用配置上去:
sudo ln -s /etc/nginx/sites-available/my_blog /etc/nginx/sites-enabled
zyp@zyp-virtual-machine:/etc/nginx/sites-available$ ls default my_blog sites zyp@zyp-virtual-machine:/etc/nginx/sites-available$ sudo ln -s /etc/nginx/sites-available/my_blog /etc/nginx/sites-enabled ... zyp@zyp-virtual-machine:/etc/nginx/sites-enabled$ ls my_blog sites
至此 Nginx 就配置好了,接下來搞定
Gunicorn
:- 安裝
Gunicorn
- 重啓
Nginx
服務 - 啓動
Gunicorn
先回到項目所在的目錄,並且進入虛擬環境,然後輸入:
(myenv) zyp@zyp-virtual-machine:~/sites/django_learning/blog_project$ pip3 install gunicorn (myenv) zyp@zyp-virtual-machine:~/sites/django_learning/blog_project$ sudo service nginx reload (myenv) zyp@zyp-virtual-machine:~/sites/django_learning/blog_project$ gunicorn --bind unix:/tmp/192.168.171.128.socket blog_project.wsgi:application
也可以用
sudo service nginx restart
,區別是 reload 只重載配置文件,restart 重啓整個服務。最後打開瀏覽器,訪問服務器查看效果。
- 安裝
9. css、js等沒有加載出來
本地調試好工程後,服務起來後在瀏覽器上測試也是正常的,但是一部署後就發現瀏覽器中沒有加載css、js等,只有單純的html格式,經過查看 nginx 的日誌發現了問題:沒有所需要的文件。
怎麼會沒有該文件呢?於是查看了 nginx 的配置文件:
server {
charset utf-8;
listen 80;
server_name 192.168.171.128; # 暫時是我本地的Ubuntu的ip地址
location /static {
root /home/zyp/sites/django_blog_tutorial/collected_static;
}
location /media {
root /home/zyp/sites/django_blog_tutorial/media;
}
location / {
proxy_set_header Host $host;
proxy_pass http://unix:/tmp/192.168.171.128.socket;
}
}
然後查看工程中 collected_static 目錄下,發現沒有 static 目錄,所需要的 css、js文件都直接在 collected_static 目錄下,也就是少了中間件一層 static。可以推理是在執行 python manage.py collectstatic
後出現的問題,排查發現在 settings.py 文件中的靜態文件收集目錄的路徑有誤:
# 靜態文件收集目錄
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
添加 static 後就可以展示 css、js等了:
# 靜態文件收集目錄
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static/static')
同理,media 目錄中存放的資源在頁面中加載不出來,也是路徑的問題。其實這邊可以修改工程中的 settings.py 文件,也可以更改 nginx 中的配置文件。
最後,終於改好了:
atic。可以推理是在執行 python manage.py collectstatic
後出現的問題,排查發現在 settings.py 文件中的靜態文件收集目錄的路徑有誤:
# 靜態文件收集目錄
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
添加 static 後就可以展示 css、js等了:
# 靜態文件收集目錄
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static/static')
同理,media 目錄中存放的資源在頁面中加載不出來,也是路徑的問題。其實這邊可以修改工程中的 settings.py 文件,也可以更改 nginx 中的配置文件。
最後,終於改好了: