Django - 从表单更新多个模型 - 数据不保存

Django - Updating multiple models from a form - Data does not saved

我有一个自定义 User 模型来管理一些配置文件用户:is_studentis_professoris_executive

此外,在这个模型中,我有 get_student_profile()get_professor_profile()get_executive_profile() 方法从我的不同视图中获取每个用户的用户配置文件数据。

class User(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(unique=True)
    username = models.CharField(max_length=40, unique=True)
    slug = models.SlugField(max_length=100, blank=True)
    is_student = models.BooleanField(default=False)
    is_professor = models.BooleanField(default=False)
    is_executive = models.BooleanField(default=False)

    def get_student_profile(self):
        student_profile = None
        if hasattr(self, 'studentprofile'):
            student_profile = self.studentprofile
        return student_profile

    def get_professor_profile(self):
        professor_profile = None
        if hasattr(self, 'professorprofile'):
            professor_profile = self.professorprofile
        return professor_profile

    def get_executive_profile(self):
        executive_profile = None
        if hasattr(self, 'executiveprofile'):
            executive_profile = self.executiveprofile
        return executive_profile

此外,每个配置文件用户 is_studentis_professoris_executive 都有自己的模型,我在其中管理他们自己的数据:

class StudentProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    slug = models.SlugField(max_length=100,blank=True)
    origin_education_school = models.CharField(max_length=128)
    current_education_school = models.CharField(max_length=128)
    extra_occupation = models.CharField(max_length=128)

class ProfessorProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    slug = models.SlugField(max_length=100,blank=True)


class ExecutiveProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL,      on_delete=models.CASCADE)
    slug = models.SlugField(max_length=100,blank=True)

就是这样,在我的 User 模型中,我覆盖了 save() 方法来表示创建的用户的 username 字段的值相等到属于配置文件用户的 slug,它将带走该用户(StudentProfileProfessorProfileExecutiveProfile):

def save(self, *args, **kwargs):
        user = super(User,self).save(*args,**kwargs)

        # Creating an user with student, professor and executive profiles
        if self.is_student and not StudentProfile.objects.filter(user=self).exists() \
            and self.is_professor and not ProfessorProfile.objects.filter(user=self).exists() \
            and self.is_executive and not ExecutiveProfile.objects.filter(user=self).exists():
            student_profile = StudentProfile(user=self)
            student_slug = self.username
            student_profile.slug = student_slug

            professor_profile = ProfessorProfile(user=self)
            professor_slug = self.username
            professor_profile.slug = professor_slug

            executive_profile = ExecutiveProfile(user=self)
            executive_slug = self.username
            executive_profile.slug = executive_slug

            student_profile.save()
            professor_profile.save()
            executive_profile.save()
        # And so for all possibles profile combinations  

对于这三个配置文件,我在其中生成了自己的字段的三个表单

class StudentProfileForm(forms.ModelForm):
    class Meta:
        model = StudentProfile
        fields = ('origin_education_school', 'current_education_school',
        'extra_occupation')

class ProfessorProfileForm(forms.ModelForm):
    class Meta:
        model = ProfessorProfile
        fields = ('occupation',)

class ExecutiveProfileForm(forms.ModelForm):
    class Meta:
        model = ExecutiveProfile
        fields = ('occupation', 'enterprise_name', 'culturals_arthistic','ecological')

我通过此 URL:

访问查看用户个人资料
url(r"^profile/(?P<slug>[\w\-]+)/$",
        views.account_profiles__update_view,
            name='profile'
    ),

在我基于函数的视图中 account_profiles__update_view() 我正在管理用户的请求,并根据以下内容创建表单实例(StudentProfileFormProfessorProfileFormExecutiveProfileForm)用户个人资料(is_studentis_professoris_executive

@login_required
def account_profiles__update_view(request, slug):
    user = request.user
    # user = get_object_or_404(User, username = slug)

    # empty list
    _forms = []
    if user.is_student:
        profile = user.get_student_profile()
        _forms.append(forms.StudentProfileForm)
    if user.is_professor:
        profile = user.get_professor_profile()
        _forms.append(forms.ProfessorProfileForm)
    if user.is_executive:
        profile = user.get_executive_profile()
        _forms.append(forms.ExecutiveProfileForm)

    # user = get_object_or_404(settings.AUTH_USER_MODEL, username = slug)

    if request.method == 'POST':
        # Create a list with all formularies in which there is some POST
        # operation. This mean if there is one, two or three profiles together
        # or individual
        formularios =[Form(data = request.POST,instance=profile) for Form in _forms]

        if all([form.is_valid() for form in formularios]):
            # Only save dato to database if all formularies that send
            # the user in their request are correct or well formed in their
            # data. Check that all formularies has been filled
            for form in formularios:
                profile = form.save(commit=False)
                profile.user = user
                profile.save()
            return redirect('dashboard')
    else:
        formularios = [Form() for Form in _forms]

    # Access to class Forms instanced (StudentProfileForm,
    # ProfessorProfileForm, ExecutiveProfileForm), through the __class__
    # pŕoperty which return the class onlying. An this class have another
    # property named __name__ which return the name of string of a class,
    # which is the same name with I did name the form classes
    # (StudentProfileForm, ProfessorProfileForm, ExecutiveProfileForm)
    # Next I call to their string method to grant that I always will return
    # a string and I call to lower.

    # The idea with this is place data into a python dictionarie and access
    # to it
    data = {form.__class__.__name__.__str__().lower(): form for form in formularios}
    data['userprofile'] = profile
    return render(request, 'accounts/profile_form.html', data,)

而我的profile_form.html模板我有以下小逻辑:

<form method="POST">
    {% csrf_token %}
    {% if userprofile.user.is_student %}
    <div align="center"><i>My Student Profile data</i></div>
        {% bootstrap_form studentprofileform %}
             {{ studentprofileform.non_field_errors }}
        {% endif %}          
        {% if userprofile.user.is_professor %}
            <div align="center"><i>My Professor Profile data</i></div>
            {% bootstrap_form professorprofileform %}
            {{ professorprofileform.non_field_errors }}
        {% endif %}
        {% if userprofile.user.is_executive %} 
            <div align="center"><i>My Executive Profile data</i></div>  
            {% bootstrap_form executiveprofileform %}
            {{ executiveprofileform.non_field_errors }}
        {% endif %}
        <input type="submit" value="Save Changes" class="btn btn-default">
</form>

我想写下所有这些细节来评论发生在我身上的以下情况:

我在我的模板中渲染了三个表单

仅存储与所选的最新用户配置文件相关的数据,这意味着:

个人资料:is_studentis_professoris_executive

此时,只考虑最新选择的配置文件用户,并根据它保存他们的相关数据,而不是其他。

这是因为在我的 account_profiles__update_view() 基于函数的视图中我有这个 if .. 句子逻辑:

if user.is_student:
    profile = user.get_student_profile()
    _forms.append(forms.StudentProfileForm)
if user.is_professor:
    profile = user.get_professor_profile()
    _forms.append(forms.ProfessorProfileForm)
if user.is_executive:
    profile = user.get_executive_profile()
    _forms.append(forms.ExecutiveProfileForm)

但是,我不知道我是否应该考虑在这里执行所有可能的配置文件用户组合并创建相同的表单实例组合。这是表演的事实吗?

我尝试将 account_profiles__update_view() 作为基于 class 的视图,here some code about it 但是配置文件和实例的特征形式发送到模板,让我选择基于函数的视图选项。 ¿最好的选择是什么?

对于这个长问题,我深表歉意,我确实想提供所有细节,以便更好地理解我想要完成的代码和方法。 我非常感谢一些支持和指导。

我冒昧地为你清理了一些东西。让我们回顾一下每一件作品。


models.py

无需使用 hasattr() 探测模型。我们更愿意使用这样一个事实,即用户和配置文件之间的 OneToOneField 将在关系的两边放置一个引用。这意味着您可以简单地覆盖您的 save() 方法,如下所示:

注意:您可能更愿意使用 Signals 创建配置文件。

from django.conf import settings
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.db import models


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    username = models.CharField(max_length=40, unique=True)
    slug = models.SlugField(max_length=100, blank=True)

    is_student = models.BooleanField(default=False, help_text="User is student")
    is_professor = models.BooleanField(default=False, help_text="User is professor")
    is_executive = models.BooleanField(default=False, help_text="User is executive")

    USERNAME_FIELD = 'email'

    def save(self, *args, **kwargs):
        super(User, self).save(*args, **kwargs)

        if self.is_student and getattr(self, 'studentprofile', None) is None:
            StudentProfile.objects.create(
                user=self,
                slug=self.username
            )

        if self.is_professor and getattr(self, 'professorprofile', None) is None:
            ProfessorProfile.objects.create(
                user=self,
                slug=self.username
            )

        if self.is_executive and getattr(self, 'executiveprofile', None) is None:
            ExecutiveProfile.objects.create(
                user=self,
                slug=self.username
            )


views.py

同样,让我们​​使用这样一个事实,即用户的 OneToOneField 将包含对配置文件的引用:

@login_required
def update_view(request):
    user = request.user

    # Populate Forms and Instances (if applicable)
    form_profiles = []
    if user.is_student:
        form_profiles.append({'form': StudentProfileForm, 'instance': user.studentprofile})
    if user.is_professor:
        form_profiles.append({'form': ProfessorProfileForm, 'instance': user.professorprofile})
    if user.is_executive:
        form_profiles.append({'form': ExecutiveProfileForm, 'instance': user.executiveprofile})

    if request.method == 'POST':
        forms = [x['form'](data=request.POST, instance=x['instance']) for x in form_profiles]

        if all([form.is_valid() for form in forms]):
            for form in forms:
                form.save()
            return redirect('dashboard')
    else:
        forms = [x['form'](instance=x['instance']) for x in form_profiles]

    return render(request, 'accounts/profile_form.html', {'forms': forms})


forms.py

您的表单可能如下所示:

class StudentProfileForm(forms.ModelForm):
    title = "Student Form"

    class Meta:
        model = StudentProfile
        fields = (
            'origin_education_school',
            'current_education_school',
            'extra_occupation',
        )


profile_form.html

<form method="post">
    {% csrf_token %}

    {% for form in forms %}
        <h1>{{ form.title }}</h1>
        {{ form }}
    {% endfor %}
    <button type="submit">Submit</button>
</form>

我仔细检查过,表格保存正确。

P.S。如果您想使用基于 Class 的视图,您可以遵循此 Gist 定义的 类:https://gist.github.com/michelts/1029336