【问题标题】:Error with inlined profile form save in django admin在 django admin 中保存内联配置文件表单时出错
【发布时间】:2020-07-17 07:59:03
【问题描述】:

我真的承认了,因为问题听起来微不足道,但我没有用谷歌搜索任何与此相关的内容。我发现了一个非常相似的问题Django Admin Create Form Inline OneToOne,但在我的情况下,我无法删除信号,它会破坏我应用程序中用户创建的正常方式。 我有模型用户和配置文件连接的 OneToOne 关系(models.py):

from django.db import models
from django.contrib.auth.models import AbstractUser

from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from .managers import UserManager

class User(AbstractUser):
    """auth/login-related fields"""
    username = None
    email = models.EmailField(unique=True)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []
    objects = UserManager()

    def __str__(self):
        return self.email 

class Profile(models.Model):
    """profile fields"""
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    about = models.CharField(max_length=2000, blank=True)
    telegram = models.CharField(max_length=25, blank=True)
    def save(self, *args, **kwargs):
        print('save profile', args, kwargs) # from admin it run 2 times, second with empty args, kwargs so PROBLEM HERE!!!
        print('save profile complete')

"""receivers to add a Profile for newly created users"""
@receiver(post_save, sender=User) 
def create_user_profile(sender, instance, created, **kwargs):
    print('create_user_profile', created, instance.id)
    if created:
        try:
            p = Profile.objects.create(user = instance)
            print('succsecc!!!!', p, p.id) ## here profile object created and got id
        except Exception as e:
            print('create profile error', e) # never prints
    else:
        instance.profile.save()

""" @receiver(post_save, sender=User) 
def save_user_profile(sender, instance, **kwargs):
    print('save_user_profile')
    try:
        instance.profile.save()
    except Exception:
            print('save profile error', Exception)
 """

这个模型在我的应用程序的前端运行良好,用户注册并填写他们的个人资料,一切正常。 当我尝试从 django 管理员创建用户时出现问题。然后我得到django.db.utils.IntegrityError: UNIQUE constraint failed: authenticate_profile.user_id.

我的 admin.py 文件如下所示:

from django.contrib import admin
from django.contrib.auth.models import Group

from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, ReadOnlyPasswordHashField
from django import forms

from django.contrib.auth import get_user_model
from .models import Profile
from .forms import SignUpForm

User = get_user_model()

class UserInline(admin.StackedInline):
    model = Profile
    can_delete = True
    verbose_name = Profile

    def save_model(self, request, obj, form, change):
        print('save_model profile', request.POST, obj, form, change) ## never prints
        super().save_model(request, obj, form, change)    

class CustomUserAdmin(UserAdmin):
    add_form = SignUpForm
    form = UserChangeForm
    model = User
    list_display = ('email', 'first_name', 'last_name', 'is_staff', 'is_active',)
    list_filter = ('email', 'first_name', 'last_name', 'is_staff', 'is_active',)
    fieldsets = (
        (None, {'fields': ('email', 'first_name', 'last_name' 'password1', 'password2')}),
        ('Permissions', {'fields': ('is_staff', 'is_active')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'first_name', 'last_name', 'password1', 'password2', 'is_staff', 'is_active')}
        ),
    )
    search_fields = ('email',)
    ordering = ('email',)
    inlines = (UserInline, )

    def save_model(self, request, obj, form, change):
        print('save_model user', request.POST, obj, form, change)
        super().save_model(request, obj, form, change)



admin.site.register(User, CustomUserAdmin)

post_save 信号中的代码成功运行,并创建配置文件,之后 django 尝试从内联形式保存配置文件,但失败并出现此错误 django.db.utils.IntegrityError: UNIQUE constraint failed authenticate_profile.user_id。从我看到的日志中,Profile.save(),当我从管理员创建用户时,调用了两次,第二次没有 args 和 kwargs。但是当我覆盖配置文件模型的保存方法时,不要在没有参数的情况下运行super().save(),我没有错误,但我无法从管理员和前端保存配置文件。所以问题在于这种方法 - 如何覆盖它以从管理员和正常工作流程中工作?提前致谢!

【问题讨论】:

  • 在您的用户 post_save 信号中,您在更新用户时调用不带参数的 profile.save()。尝试删除它。
  • 问题在于,由于您有一个内联配置文件,因此您在保存用户时会显式创建它。这是一个新的,而不是现有的,因为当表单被实例化时,没有现有的配置文件可供使用。所以你想要的东西是行不通的。您不能从管理员同时创建用户和个人资料。或者你不应该自动创建一个空的配置文件,只在实际需要时创建它。
  • Johan Schiff 删除 profile.save() 并没有改变任何东西,仍然是同样的错误

标签: python django django-admin


【解决方案1】:

这很棘手......我想你忘记了 verbose_name 周围的引号。应该是

class UserInline(admin.StackedInline):
    model = Profile
    can_delete = True
    verbose_name = 'Profile'

【讨论】:

  • 它没有改变任何东西
  • 我无法重现比。我的设置与您的类似,并且可以正常工作
【解决方案2】:

唯一的电子邮件字段会给您带来我建议使用的问题

class UserRegisterForm(UserCreationForm):
email =forms.EmailField(label='Your Email')
class Meta:
    model= User
    fields = ['username','email','password1', 'password2']
    
def clean_email(self):
    email = self.cleaned_data.get("email")
    user_count = User.objects.filter(email=email).count()
    if user_count > 0:
        raise forms.ValidationError('This email has already been taken')

【讨论】:

    【解决方案3】:

    我遇到了同样的问题并找到了解决方法。我把数据录入分为两步。

    在管理中,我首先只创建User 对象,然后使用信号创建Profile 对象。只有当两个对象都创建时,我才会显示UserInline(名称ProfileInline会更准确)。

    class CustomUserAdmin(UserAdmin):
        model = User
    
      def get_inlines(self, request, obj=None):
          if obj:
              return [UserInline,]
          else:
              return []
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-20
      • 2011-10-22
      • 1970-01-01
      • 2015-12-22
      • 2011-10-06
      • 2011-07-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多