需求分析:
實現網站搭建的過程:首先進行網站的需求性分析
網站可分為前臺系統和后臺系統,由不同的功能拆分為不同的模塊
如下是一個電商網站可以拆分出的模塊:
在編寫代碼前,我們要先對網站進行架構,通過分層設計網站的不同組件,軟件開發人員可以選擇修改和添加應用層,而非全局作出更改
如下是經典的三層結構:
接入層:提供靜態內容的web前端服務器和部分動態緩存內容。
邏輯層:生成動態內容的應用服務器。
存儲層:提供數據存儲的服務器
TIPS:
為什么之前在用Django的時候直接runserver就好了呢?因為Django的開發服務器(runserver)是一個輕量級服務器,能夠處理HTTP請求和提供靜態資源服務,但主要應用于調試階段,由于單線程且沒有負載均衡SSL加密等功能,在生產環境中我們需要專業的接入層,像是Nginx
模塊引入:
為了確保功能的實現,我們可以引入所需的功能模塊
其中,installed_app這個設置告訴用戶在該項目中安裝了哪些程序,可以是第三方或自定義的,也可以是django自帶的
middleware這個設置定義了處理請求的中間件和響應件,中間件是處理請求和響應的鉤子,可用于請求到達視圖/響應返回客戶段前執行特定的操作。
在構建網站的過程中,我們要把不同需求拆解為功能模塊的同時為其添加數據庫表結構
首先,我們來進行用戶模塊的構建:
INSTALLED_APPS = [# 其他已安裝的應用...'django.contrib.auth', ?# 用戶驗證框架和模型'django.contrib.contenttypes', ?# 權限管理]MIDDLEWARE = [# 其他中間件...'django.contrib.sessions.middleware.SessionMiddleware', ?# 會話管理系統'django.contrib.auth.middleware.AuthenticationMiddleware', ?# 用戶驗證模型]
這是利用了Django自帶的用戶模塊,配置當中的.就是/的意思,暗含代碼路徑在django/contrib/auth/moddles.py,模型里面包含這些內容
讓我們先來了解一下用戶模塊:
django.contrib.auth 模塊為我們提供了用戶認證系統的核心功能,包括用戶模型、表單、視圖、URL 配置等,這些功能已經預先定義好,可以直接使用或根據需要進行擴展。它是一個完整的用戶認證框架,旨在幫助開發者快速實現用戶注冊、登錄、權限管理等功能,而無需從頭開始編寫這些代碼。
Django 自帶用戶認證模塊提供的功能
以下是 Django 自帶用戶認證模塊(django.contrib.auth)為你預先定義好的內容:
----
1. 用戶模型(User)
Django 提供了一個默認的用戶模型 User,定義在 django.contrib.auth.models 中。它包含以下字段:
? ?username:用戶名
? ?password:密碼(加密存儲)
? ?email:郵箱
? ?first_name 和 last_name:用戶姓名
? ?is_active:是否激活
? ?is_staff:是否是管理員
? ?is_superuser:是否是超級用戶
? ?groups 和 user_permissions:用戶組和權限
你可以直接使用這個默認的用戶模型,或者通過繼承 AbstractUser 或 AbstractBaseUser 來擴展它。
----
2. 表單(forms)
Django 提供了一些內置的表單類,用于用戶注冊、登錄、密碼修改等功能:
? ?UserCreationForm:用戶注冊表單
? ?AuthenticationForm:用戶登錄表單
? ?PasswordChangeForm:密碼修改表單
? ?SetPasswordForm:設置密碼表單
? ?PasswordResetForm:密碼重置表單
這些表單類已經預定義了字段和驗證邏輯,你可以直接使用,也可以通過繼承它們來自定義字段或驗證規則。
----
3. 視圖(views)
Django 提供了一些內置的視圖函數,用于處理用戶認證相關的操作:
? ?login:用戶登錄
? ?logout:用戶登出
? ?password_change:密碼修改
? ?password_reset:密碼重置
? ?password_reset_done:密碼重置完成
? ?password_reset_confirm:密碼重置確認
? ?password_reset_complete:密碼重置完成提示
這些視圖函數可以直接在 URL 配置中使用,或者通過繼承 View 類來自定義視圖邏輯。
----
4. URL 配置(urls)
Django 提供了一個 auth 應用的 URL 配置模塊 django.contrib.auth.urls,它包含了用戶認證相關的 URL 路由,例如:
from django.contrib.auth import views as auth_viewsurlpatterns = [path('login/', auth_views.LoginView.as_view(), name='login'),path('logout/', auth_views.LogoutView.as_view(), name='logout'),path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(), name='password_change_done'),path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'),path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),]
?
你可以直接在項目的 urls.py 文件中包含這些 URL 路由,或者根據需要自定義 URL 路徑和視圖參數。
----
5. 中間件和會話管理
Django 的認證系統還提供了中間件(如 AuthenticationMiddleware)和會話管理功能,用于在請求中自動處理用戶認證信息,并將用戶對象綁定到 request.user。
----
擴展:
在很多時候我們都需要對Django自帶的模型進行相應的拓展
關聯表:
這個時候可以使用關聯表
用如下的onetoonefield方法創建:
from django.db import modelsfrom django.contrib.auth.models import Userclass Profile(models.Model):GENDER_CHOICES = (('M', 'Male'), ?# 男性('F', 'Female'), ?# 女性)user = models.OneToOneField(User, on_delete=models.CASCADE)gender = models.CharField(max_length=1, choices=GENDER_CHOICES) ?# 性別birth_date = models.DateField(null=True, blank=True) ?# 出生日期
?
模型字段解釋
1. ?GENDER_CHOICES:
? ?這是一個元組列表,用于定義性別字段的選項。每個元組包含兩個元素:第一個是存儲在數據庫中的值,第二個是顯示給用戶的值。
2. ?user:
? ?這是一個 OneToOneField 字段,它建立了 Profile 模型和 User 模型之間的一對一關系。
? ?User 是 Django 內置的用戶模型,通常包含用戶名、密碼、電子郵件等基本信息。
? ?on_delete=models.CASCADE 參數指定了當關聯的 User 實例被刪除時,相關的 Profile 實例也會被級聯刪除。
3. ?gender:
? ?這是一個 CharField 字段,用于存儲用戶的性別。
? ?max_length=1 指定了性別字段的最大長度為1個字符。
? ?choices=GENDER_CHOICES 參數將性別字段的值限制為在 GENDER_CHOICES 中定義的選項。
4. ?birth_date:
? ?這是一個 DateField 字段,用于存儲用戶的出生日期。
? ?null=True 允許該字段在數據庫中存儲為 NULL,這意味著它可以為空。
? ?blank=True 允許在 Django 管理后臺或表單中不填寫該字段。
關聯表的使用
在 Django 中,當你使用 OneToOneField 時,Django 會在數據庫中創建一個新的表來存儲 Profile 模型的數據。這個表會包含一個額外的字段(通常是 user_id),這個字段是一個外鍵,指向 User 表的主鍵。
例如,如果你的 User 表的主鍵是 id,那么 Profile 表可能會包含以下字段:
? ?id:Profile 表的主鍵。
? ?user_id:外鍵,指向 User 表的 id 字段。
? ?gender:存儲性別的字段。
? ?birth_date:存儲出生日期的字段。
這種設計允許每個 User 實例都有一個對應的 Profile 實例,而每個 Profile 實例只能屬于一個 User 實例。這種一對一的關系在數據庫中通過外鍵約束來實現,確保了數據的一致性和完整性。
總結
這段代碼通過 OneToOneField 創建了一個 Profile 模型,它與 User 模型建立了一對一的關系。這種關系通過在 Profile 表中添加一個外鍵字段來實現,該字段指向 User 表的主鍵。這種設計使得每個用戶都有一個相關的個人資料,而每個個人資料只屬于一個用戶。
信號定義:
在創建用戶過程中多次會用到創建用戶的代碼,為了避免重復創建,我們將定義一個信號,在創建用戶時自動創建檔案:
from django.apps import AppConfigclass MyAppConfig(AppConfig):name = 'myapp'def ready(self):import myapp.signals
?
?該方法使用要在myapp下創建一個signals.py文件并且在app.py文件當中重寫ready方法來連接信號,以及更新installed_apps
INSTALLED_APPS = [...'myapp.apps.MyAppConfig',...]
TIPS:當然,signals的內容也可以直接寫在models里面
然后我們在更改完models之后再去更改視圖和模版文件
模板:
<h2>{{ user.get_full_name }}</h2><ul><li>Username: {{ user.username }}</li> ?<!-- 顯示用戶名 --><li>Location: {{ user.profile.gender }}</li> ?<!-- 顯示性別 --><li>Birth Date: {{ user.profile.birth_date }}</li> ?<!-- 顯示出生日期 --></ul>
視圖:
def update_profile(request, user_id):user = User.objects.get(pk=user_id) ?# 獲取用戶對象user.profile.gender = 'M' ?# 設置性別user.save() ?# 保存
視圖函數當中反應的是對于用戶檔案的更新:在更新用戶檔案時,瀏覽器會先上傳一個表單,然后由服務器來處理這個表單,處理的方法就是視圖函數當中的先獲取數據對象,調用先前定義的模型進行修改,然后再調用save方法進行更新
注意!!:這里儲存的是模型的字段,因此不能直接儲存到數據庫,我們需要使用forms.ModelForm類創建表單
from django import formsclass UserForm(forms.ModelForm):class Meta:model = User ?# 關聯User模型fields = ('first_name', 'last_name', 'email') ?# 表單字段class ProfileForm(forms.ModelForm):class Meta:model = Profile ?# 關聯Profile模型fields = ('url', 'gender', 'birth_date') ?# 表單字段
然后再編寫對應用戶登陸的視圖
from django.contrib import messagesfrom django.shortcuts import redirect, renderfrom django.db import transactionfrom .forms import UserForm, ProfileForm@login_required ?# 驗證登錄@transaction.atomicdef update_profile(request):if request.method == 'POST': ?# POST請求user_form = UserForm(request.POST, instance=request.user) ?# 獲取用戶表單信息profile_form = ProfileForm(request.POST, instance=request.user.profile) ?# 獲取表單信息if user_form.is_valid() and profile_form.is_valid(): ?# 驗證通過則保存信息user_form.save()profile_form.save()messages.success(request, _('Your profile was successfully updated!'))return redirect('settings:profile')else: ?# 驗證不通過則顯示錯誤信息messages.error(request, _('Please correct the error below.'))else: ?# 其他請求,一般是GET請求user_form = UserForm(instance=request.user)profile_form = ProfileForm(instance=request.user.profile)# 返回表單頁面return render(request, 'profiles/profile.html', {'user_form': user_form,'profile_form': profile_form})
如上代碼當中,用戶如果用的是post方法,則獲取表單信息與之對照,如果是get方式則返回表單頁面,并將內容填入表單,(對應的,我們需要更改其模版文件)
<form method="post">{% csrf_token %}{{ user_form.as_p }} ?<!-- 渲染用戶表單 -->{{ profile_form.as_p }} ?<!-- 渲染Profile表單 --><button type="submit">Save changes</button> ?<!-- 提交按鈕 --></form>
繼承:
如果我們的用戶系統完全不需要某一字段,也可以使用繼承的方式(AbstractBaseUser)
from __future__ import unicode_literalsfrom django.db import modelsfrom django.core.mail import send_mailfrom django.contrib.auth.models import PermissionsMixinfrom django.contrib.auth.base_user import AbstractBaseUserfrom django.utils.translation import ugettext_lazy as _from .managers import UserManagerclass User(AbstractBaseUser, PermissionsMixin):GENDER_CHOICES = (('M', 'Male'), ?# 男性('F', 'Female'), ?# 女性)gender = models.CharField(max_length=1, choices=GENDER_CHOICES) ?# 性別birth_date = models.DateField(null=True, blank=True) ?# 出生年月email = models.EmailField(_('email address'), unique=True) ?# 電子郵件first_name = models.CharField(_('first name'), max_length=30, blank=True)last_name = models.CharField(_('last name'), max_length=30, blank=True)date_joined = models.DateTimeField(_('date joined'), auto_now_add=True) ?# 注冊時間is_active = models.BooleanField(_('active'), default=True) ?# 是否活躍avatar = models.ImageField(upload_to='avatars/', null=True, blank=True) ?# 用戶頭像objects = UserManager()USERNAME_FIELD = 'email'REQUIRED_FIELDS = []class Meta:verbose_name = _('user')verbose_name_plural = _('users')def get_full_name(self): ?# 獲取用戶全名full_name = '%s %s' % (self.first_name, self.last_name)return full_name.strip()def get_short_name(self):return self.first_namedef email_user(self, subject, message, from_email=None, **kwargs): ?# 向用戶發送電子郵件send_mail(subject, message, from_email, [self.email], **kwargs)
在使用繼承AbstractBaseUser時,有很多相關的限制(建議直接重新自己寫)
?
如果要添加某一些字段,可以采用繼承AbstractUser方法,運營此法可對于原有的類進行更改
from django.db import modelsfrom django.contrib.auth.models import AbstractUserclass User(AbstractUser):GENDER_CHOICES = (('M', 'Male'), ?# 男性('F', 'Female'), ?# 女性)gender = models.CharField(max_length=1, choices=GENDER_CHOICES) ?# 性別birth_date = models.DateField(null=True, blank=True) ?# 出生年月
?
?
模塊設計的大概步驟:
然后我們再來進行商品庫模塊的設計:
首先我們設計了類別模型、商品模型以及類別和商品的多對多的模型
from django.db import modelsclass Category(models.Model):name = models.CharField('Name', max_length=255, db_index=True) ?# 類別名稱description = models.TextField('Description', blank=True) ?# 類別描述products = models.ManyToManyField('Product') ?# 多對多關系class Product(models.Model):title = models.CharField('Title') ?# 商品名稱description = models.TextField('Description', blank=True) ?# 商品描述attributes = models.TextField('Attribute', blank=True) ?# 商品附屬信息date_created = models.DateTimeField() ?# 商品創建時間
在上述代碼中,可以把manytomany理解為一個聲明,表示兩個表要建聯,而ProductCategory則可以視作聲明的具體內容
from django.http import HttpResponsefrom .models import Product, Categorydef get_product_detail(request, product_id): ?# 獲取商品詳情return Product.objects.get(pk=product_id)def get_all_products(request, category_id): ?# 通過類別獲取商品列表return Category.objects.get(pk=category_id).products.all()
定義相應的視圖函數(根據搜索返回商品的過程,實際搜索當中要比這復雜的多因為用戶的描述和實際商品的名稱并非完全相同,此時也要給出最合理的結果)
{% for product in productions %}<p>{{ product.title }}</p> ?<!-- 商品的名稱 --><p>{{ product.description }}</p> ?<!-- 商品的描述 -->{% endfor %}
并給出相應的模版文件(這里用了for標簽來渲染列表)
總結:
總之,網站的制作就是先進行需求分析,根據需求分析得到各個功能模塊,然后對每一個模塊進行編寫,編寫的順序為:模型文件再到視圖函數及其對應的模版文件,urls.py和settings.py的兩個視圖函數的編寫可以放在這一步之前也可以是之后,但是為了方便,編寫settings.py之前我們最好還是要明確自己要用到的所有models和middleware
?