記錄學習一個django項目時遇到的bug及處理方法

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檢查沒有展示的元素

模板中已經寫好前端的展示,視圖函數也給前端傳參了,但是實際就是沒有顯示。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-dTMGd3zV-1592238552874)(others/somebug_6.png)]
通過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 上部署測試。

  1. 修改 Django 的配置文件

    # my_blog/settings.py
    
    # 關閉調試模式
    DEBUG = False
    
    # 允許的服務器
    ALLOWED_HOSTS = ['*']
    
    # 靜態文件收集目錄
    STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
    
  2. 虛擬環境一般是需要在服務器上重新生成的

    安裝包:

    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 的日誌發現了問題:沒有所需要的文件。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JMGJjnzm-1592238552897)(others/somebug_nginx_error.png)]
怎麼會沒有該文件呢?於是查看了 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 中的配置文件。

最後,終於改好了:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-CwmyCMDO-1592238552910)(others/somebug_blog_show.png)]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章