第四章下半部分 Django by example

用户注册和用户profiles

现有的用户已经可以登录,登出,修改他们的密码,以及当他们忘记密码的时候重置他们的密码。现在,我们需要构建一个视图(view)允许访问者创建他们的账号。

用户注册

先创建一个表单,供填写用户名、密码等

from django.contrib.auth.models import User
class UserRegistrationForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput, label='Password')  # 新增加的表单字段
    password2 = forms.CharField(label='Repeat password', widget=forms.PasswordInput)  # 新增加的表单字段

    class Meta:
        model = User  # 模型表单使用User模型
        fields = ('username', 'first_name', 'last_name',)  # 表单内容使用元组内的字段
    def clean_password2(self):# 自定义的表单验证,函数命名规则clean_<fieldname>,当通过调用is_valid()方法验证这个表单(form)时这个检查会被执行。
        cd = self.cleaned_data
        if cd['password'] != cd['password2']:
            raise forms.ValidationError('两次密码不同')
        return cd['password2']
# 表单(forms)还包含了一个clean()方法用来验证表单(form)的所有内容,这对验证需要依赖其他字段的字段是非常有用的。

# Django还提供一个UserCreationForm表单(form)给你使用,它位于django.contrib.auth.forms非常类似与我们刚才创建的表单(form)

#表单(forms)还包含了一个clean()方法用来验证表单(form)的所有内容,这对验证需要依赖其他字段的字段是非常有用的。
Django还提供一个UserCreationForm表单(form)给你使用,它位于django.contrib.auth.forms非常类似与我们刚才创建的表单(form)

写registration注册视图

...import省略
def register(request):
    if request.method == 'POST':
        user_form = UserRegistrationForm(request.POST)
        if user_form.is_valid():
            cd = user_form.cleaned_data#获得字典
            password = cd['password']#获取password field
            new_user = user_form.save(commit=False)# 获取User实例new_user,不提交
            new_user.set_password(password)#nwe_user对象设置密码
            new_user.save()#提交
            return render(request,'account/register_done.html',{'new_user': new_user})#渲染到register_done
    else:
        user_form = UserRegistrationForm()
    return render(request, 'account/register.html',{'user_form': user_form})#空表单渲染到注册页

在urls中配置

url(r'^register/$', views.register, name='register'),

html文件

//register.html
{% extends "base.html" %}
{% block title %}Create an account{% endblock %}
{% block content %}
  <h1>Create an account</h1>
  <p>Please, sign up using the following form:</p>
  <form action="." method="post">
    {{ user_form.as_p }}
    {% csrf_token %}
    <p><input type="submit" value="Create my account"></p>
  </form>
{% endblock %}
//register_done.html
{% extends "base.html" %}
{% block title %}Welcome{% endblock %}
{% block content %}
  <h1>Welcome {{ new_user.first_name }}!</h1>
  <p>Your account has been successfully created. Now you can <a href="{% url "login" %}">log in</a>.</p>
{% endblock %}

可以在login.html中添加注册超链接


扩展User的方法

有两种方式来扩展user模型,一种是继承AbstractUser,重写User类,还有一种方式,与django自带的User模型进行OneToOne关联(一对一关联)

为了保持你的代码通用化,当需要定义模型(model)和用户模型的关系时,使用get_user_model()方法来取回用户模型(model)并使用AUTH_USER_MODEL设置来引用这个用户模型,替代直接引用auth的User模型(model)。

from django.db import models
from django.conf import settings


class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)#如果你没有指定OneToOneField 的related_name 参数,Django 将使用当前模型的小写的名称作为默认值。比如要反向查询User对象的Profile ,使用单个user.profile,如果有related_name,使用user.<RELATED_NAME>.
    date_of_birth = models.DateField(blank=True, null=True)
    photo = models.ImageField(upload_to='users/%Y/%m/%d', blank=True)#上传图片
    
    def __str__(self):
        return 'Profile for user {}'.format(self.user.username)

user一对一字段允许我们关联用户和profiles。photo字段是一个ImageField字段。你需要安装一个Python包来管理图片,使用PIL(Python Imaging Library)或者Pillow(PIL的分叉),pip安装Pillow包。

为了Django能在开发服务中管理用户上传的多媒体文件,在项目setting.py文件中添加如下设置:
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')

MEDIA_URL 是管理用户上传的多媒体文件的主URL,MEDIA_ROOT是这些文件在本地保存的路径。我们动态的构建这些路径相对我们的项目路径来确保我们的代码更通用化。

现在,编辑bookmarks项目中的主urls.py文件,修改代码如下所示:

from django.conf.urls import include, url
from django.contrib import admin
from django.conf import settings
from django.conf.urls.static import static


urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^account/', include('account.urls')),
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL,
                        document_root=settings.MEDIA_ROOT)

在这种方法中,Django开发服务器将会在开发时改变对多媒体文件的服务。
static()帮助函数最适合在开发环境中使用而不是在生产环境使用。绝对不要在生产环境中使用Django来服务你的静态文件。
之后,在admin中注册以及数据库迁移
创建表单给用户可编辑

forms文件
from .models import Profile

class UserEditForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ('first_name', 'last_name', 'email')
        
class ProfileEditForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ('date_of_birth', 'photo')

这两个表单(forms)的功能:

  • UserEditForm:允许用户编辑它们的first name,last name, e-mail 这些储存在User模型(model)中的内置字段。
  • ProfileEditForm:允许用户编辑我们存储在定制的Profile模型(model)中的额外数据。用户可以编辑他们的生日数据以及为他们的profile上传一张照片。
    创建views文件
视图文件
from .forms import LoginForm, UserRegistrationForm, \
UserEditForm, ProfileEditForm
@login_required
def edit(request):
    if request.method == 'POST':
        user_form = UserEditForm(instance=request.user,
                                data=request.POST)
        profile_form = ProfileEditForm(instance=request.user.profile,
                                        data=request.POST,
                                        files=request.FILES)
        if user_form.is_valid() and profile_form.is_valid():
            user_form.save()
            profile_form.save()
            return redirect('dashboard')
        #redirect使用文档https://docs.djangoproject.com/en/dev/topics/http/shortcuts/
    else:
        user_form = UserEditForm(instance=request.user)
        profile_form = ProfileEditForm(instance=request.user.profile)
    return render(request,
                 'account/edit.html',
                 {'user_form': user_form,
                 'profile_form': profile_form})

创建url规则

url(r'^edit/$', views.edit, name='edit'),

最后,在templates/account/中创建一个新的模板(template)命名为edit.html,为它添加如下内容:

{% extends "base.html" %}
{% block title %}Edit your account{% endblock %}
{% block content %}
    <h1>Edit your account</h1>
    <p>You can edit your account using the following form:</p>
    <form action="." method="post" enctype="multipart/form-data">
        {{ user_form.as_p }}
        {{ profile_form.as_p }}
        {% csrf_token %}
    <p><input type="submit" value="Save changes"></p>
    </form>
{% endblock %}

我们在表单(form)中包含enctype="multipart/form-data"用来支持文件上传。我们使用一个HTML表单来提交两个表单(forms): user_form和profile_form。

使用一个定制User模型(model)

Django还提供一个方法可以使用你自己定制的模型(model)来替代整个User模型(model)。你自己的用户类需要继承Django的AbstractUser类,这个类提供了一个抽象的模型(model)用来完整执行默认用户。你可访问 https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#substituting-a-custom-user-model来获得这个方法的更多信息。
使用一个定制的用户模型(model)将会带给你很多的灵活性,但是它也可能给一些需要与User模型(model)交互的即插即用的应用集成带来一定的困难。

使用messages框架

一次性展示信息的框架
from django.contrib import messages
常用方法:

  • success():当操作成功后显示成功的messages
  • info():展示messages
  • warning():某些还没有达到失败的程度但已经包含有失败的风险,警报用
  • error():操作没有成功或者某些事情失败
  • debug():在生产环境中这种messages会移除或者忽略
    html文件添加
{% if messages %}
 <ul class="messages">
   {% for message in messages %}
     <li class="{{ message.tags }}">
    {{ message|safe }}
        <a href="#" class="close"> </a>
     </li>
   {% endfor %}
 </ul>
{% endif %}

views中添加

from django.contrib import messages
@login_required
def edit(request):
    if request.method == 'POST':
    # ...
        if user_form.is_valid() and profile_form.is_valid():
            user_form.save()
            profile_form.save()
            messages.success(request, 'Profile updated '\
                                     'successfully')
        else:
            messages.error(request, 'Error updating your profile')
    else:
        user_form = UserEditForm(instance=request.user)
        # ...

创建一个定制的认证(authentication)后台

Django允许你通过不同的来源进行认证(authentication)。AUTHENTICATION_BACKENDS(默认设置,并不在settings文件中显示的显示出来)设置包含了所有的给你的项目的认证(authentication)后台。默认的,这个设置如下所示:

('django.contrib.auth.backends.ModelBackend',)

https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#other-authentication-sources
当你使用django.contrib.auth的authenticate()函数,Django会通过每一个定义在AUTHENTICATION_BACKENDS中的后台一个接一个地尝试认证(authentication)用户,直到其中有一个后台成功的认证该用户才会停止进行认证。只有所有的后台都无法进行用户认证(authentication),他或她才不会在你的站点中通过认证(authentication)。
Django提供了一个简单的方法来定义你自己的认证(authentication)后台。一个认证(authentication)后台就是提供了如下两种方法的一个类:

  • authenticate():将用户信息当成参数,如果用户成功的认证(authentication)就需要返回True,反之,需要返回False。
  • get_user():将用户的ID当成参数然后需要返回一个用户对象。

创建一个定制认证(authentication)后台非常容易,就是编写一个Python类实现上面两个方法。我们要创建一个认证(authentication)后台让用户在我们的站点中使用他们e-mail替代他们的用户名来进行认证(authentication)

在应用路径下创建authentication文件

from django.contrib.auth.models import User


class EmailAuthBackend(object):
    def authenticate(self,username=None, password=None):
        try:
            user = User.objects.get(email=username)
            if user.check_password(password):
                return user
            return None
        except User.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

在settings中添加认证后台


AUTHENTICATION_BACKENDS = (
   'django.contrib.auth.backends.ModelBackend',
   'account.authentication.EmailAuthBackend',
)

这样,就可以在登陆中,使用邮箱来登录了。


为你的站点添加社交认证(authentication)

1.需要安装python-social-auth模块
2.在settings中添加应用

INSTALLED_APPS = (
    #...
    'social.apps.django_app.default',
)

3.数据迁移
4.在主urls中设置

url('social-auth/',
    include('social.apps.django_app.urls', namespace='social')),

5.为了确保社交认证(authentication)可以工作,你还需要配置一个hostname,因为有些服务不允许重定向到127.0.0.1或localhost。为了解决这个问题,在Linux或者Mac OSX下,编辑你的/etc/hosts文件添加如下内容:

127.0.0.1 mysite.com

这是用来告诉你的计算机指定mysite.com hostname指向你的本地机器。如果你使用Windows,你的hosts文件在 C:\Winwows\ System32\Drivers\etc\hosts。

为了验证你的host重定向是否可用,在浏览器中打开 http://mysite.com:8000/account/login/ 。如果你看到你的应用的登录页面,host重定向已经可用。


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容