django中使用Profile扩展User模块(基于django 1.10版本下)

Posted ShaunChen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了django中使用Profile扩展User模块(基于django 1.10版本下)相关的知识,希望对你有一定的参考价值。

版本:Django 1.10.1(其他版本可能有不同的实现好解决办法)

参考官方文档:https://docs.djangoproject.com/en/1.10/topics/auth/customizing/

在开发过程中,Django的用户管理模块能够给我们带来非常大的便利,但是Django的User模块所提供的字段太少,所以对User模块的扩展是必须的,下面结合我自己的开发过程中,使用Profile扩展User模块时遇到的问题以及解决的方法进行记录。

先看一段我根据官方文档最先开发完成的代码:

admin.py

from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin

from models import *

class AccountInline(admin.StackedInline):
    model = Account
    can_delete = False
    verbose_name_plural = \'account\'
    

class UserAdmin(BaseUserAdmin):
    inlines = (AccountInline, )


admin.site.unregister(User)
admin.site.register(User, UserAdmin)

model.py:

from __future__ import unicode_literals

from mysite import settings
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save


class Account(models.Model):
    """
    Registered users
    """
    SEX_CHOICES = {
        1: \'boy\',
        2: \'girl\',
    }
    
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    sex = models.SmallIntegerField(default=1, choices=SEX_CHOICES.items())
    birth = models.DateField(blank=True, null=True)
    age = models.SmallIntegerField(blank=True,null=True)
    contact_number = models.CharField(max_length=128, blank=True, null=True)
    personalized_signature = models.CharField(max_length=128, blank=True, null=True)
    picture = models.ImageField(upload_to="Image/", blank=True,null=True)
    openid = models.CharField(max_length=128, blank=True, null=True)
        
    def __unicode__(self):
        return self.user.username
    
    class Meta:
        db_table = \'Account\'

def create_user_profile(sender, instance, created, **kwargs):
    if created:
        profile = Account()
        profile.user = instance
        profile.save()
        
post_save.connect(create_user_profile, sender=User)

settings.py:

INSTALLED_APPS = [
    \'django.contrib.admin\',
    \'django.contrib.auth\',
    \'django.contrib.contenttypes\',
    \'django.contrib.sessions\',
    \'django.contrib.messages\',
    \'django.contrib.staticfiles\',
    \'rest_framework\',
    \'Blog\'
]
AUTH_PROFILE_MODULE = \'Blog.Account\'

这段是官方文档给出的参考方法:可以很好的让你自己的model跟User模块进行一对一的外键映射。

我们可以通过admin页面进行用户的创建,可以看到我们的Account模型很好的嵌入了User模块,然后可以对User模块进行字段的扩张。

但是此时,当你保存的时候,出现了问题,请看截图:

这个问题告诉我(仅仅是我的理解,如果有错误希望留言提出),当django创建完User表中的用户时,通过Userprofile去创建第二个实例(Account)的时候,执行save操作的时候,执行了俩次,从而导致上面的user_id已经存在无法完成操作,这个原因在于:上面model中调用post_save发生了俩次(django自己的save一次,然后通过信号post_save一次,导致第二个调用save的时候发现已经存在了相同的主键ID)

所以我们需要做的就是对model.py文件中的save进行重写,重写之后的model.py文件为:

class Account(models.Model):
    """
    Registered users
    """
    SEX_CHOICES = {
        1: \'boy\',
        2: \'girl\',
    }
    
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    sex = models.SmallIntegerField(default=1, choices=SEX_CHOICES.items())
    birth = models.DateField(blank=True, null=True)
    age = models.SmallIntegerField(blank=True,null=True)
    contact_number = models.CharField(max_length=128, blank=True, null=True)
    personalized_signature = models.CharField(max_length=128, blank=True, null=True)
    picture = models.ImageField(upload_to="Image/", blank=True,null=True)
    openid = models.CharField(max_length=128, blank=True, null=True)
        
    def __unicode__(self):
        return self.user.username
    
    class Meta:
        db_table = \'Account\'

    def save(self, *args, **kwargs):
        if not self.pk:
            try:
                p = Account.objects.get(user=self.user)
                self.pk = p.pk
            except Account.DoesNotExist:
                pass

        super(Account, self).save(*args, **kwargs)


def create_user_profile(sender, instance, created, **kwargs):
    if created:
        profile = Account()
        profile.user = instance
        profile.save()
        
post_save.connect(create_user_profile, sender=User)

这样就会在save创建的时候,对主键pk进行检测然后保存。

因为我在开发的时候用到了django-rest-framework,所以含有serializer序列化文件,在这里一并给出:

serializers.py:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("id", "username", "password", "email", "last_login", "date_joined")


class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ("user_id", "user", "sex", "age", "birth", "picture", "contact_number", "personalized_signature", "openid")

  

 

以上是关于django中使用Profile扩展User模块(基于django 1.10版本下)的主要内容,如果未能解决你的问题,请参考以下文章

在 Django 中使用自定义字段扩展用户模型

扩展Django内置的auth模块代码示例

Django“没有这样的列:mains_profile_friends.user_id”

如何避免 Python 模块系统中的命名冲突?

Django框架之 auth认证模块

django.db.utils.IntegrityError:NOT NULL 约束失败:users_profile.user_id