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

时间:2017-04-12 18:19:02

标签: python-3.x django-models django-forms django-views django-class-based-views

我有一个自定义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(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

  • 拥有is_student个人资料的用户...保存数据
  • 拥有is_professor个人资料的用户...保存数据
  • 拥有is_executive个人资料的用户...保存数据

  • 拥有is_studentis_professor个人资料的用户:仅将数据保存到is_professor个人资料。不在is_student个人资料表单字段

  • 中保存数据
  • 拥有is_studentis_executive个人资料的用户:仅将数据保存到is_executive个人资料。不在is_student个人资料表单字段

  • 中保存数据
  • 拥有is_professoris_executive个人资料的用户:仅将数据保存到is_executive个人资料。不在is_professor个人资料表单字段

  • 中保存数据
  • 拥有is_studentis_professoris_executive个人资料的用户:仅将数据保存到is_executive个人资料。不保存is_professoris_student个人资料表单字段

  • 中的数据

此时,只会考虑最新选择的个人资料用户,并根据它保存他们的相关数据,而不是其他。

这是因为在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)

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

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

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

1 个答案:

答案 0 :(得分:1)

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

<小时/>

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。如果您想使用基于类的视图,可以按照本要点定义的类进行操作:https://gist.github.com/michelts/1029336