如何在Django 2.0+中简单有效地创建相关对象?

时间:2019-04-24 10:43:45

标签: django django-models django-forms django-views

我想找到一种创建子对象的简单而可靠的方法。我认为这是一个简单的问题,可能使用Django RelationshipManager或Related Objects引用解决了。

过去我已经开始使用它了(通过付钱给fiver的人来帮助我解决这个问题),但是我觉得有一种更简单的方法可以使我逃脱。

这适用于我的 views.py

class MainVisitForm(SingleObjectMixin, FormView):
    template_name = "clincher/visit_form.html"
    form_class = VisitForm
    model = Main

    def post(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponseForbidden()
        self.object = self.get_object()



        form=self.get_form()
        form.fk_visit_user = self.request.user
        form.fk_visit_main = Main.objects.get(id=self.kwargs['pk'])
        #added this to save form as we are mixxing the two forms and Models
        # as the approch of singleObjectMixin is we should get object from DB as per request url as a primary key
        #and we have defined  model as a Main but taking the form view of VistForm as the probem occures
        # as i think


        if form.is_valid():
            instance = Main()
            instance.firstname = form.cleaned_data['firstname']
            instance.middelname = form.cleaned_data['middlename']
            instance.lastname = form.cleaned_data['lastname']
            instance.date_of_birth = form.cleaned_data['date_of_birth']
            instance.sex = form.cleaned_data['sex']

            instance.address = form.cleaned_data['address']
            instance.save()

        return super().post(request, *args, **kwargs)


    def get_success_url(self):
        return reverse('clincher:main_detail', kwargs={'pk': self.object.pk})

基本上,当用户位于“主要”对象的详细信息页面中时,我希望他们能够创建子对象(访问对象)。最终,1位患者将进行多次就诊(1:m关系)。每次患者访问该文档时,都会添加与该人相关的1次新访问。稍后,我将显示该患者的就诊列表(但不是此问题的主题)。

Models.py

class Main(models.Model):
    firstname = models.CharField(max_length = 256, verbose_name=('First Name'))
    middlename = models.CharField(max_length=256, verbose_name=('Middle Name'))
    lastname = models.CharField(max_length=256, verbose_name=('Last Name'))
    date_of_birth = models.DateField()
    age = models.CharField(max_length=4)
    sex_list = (
        (str(1), 'Female'),
        (str(2), 'Male'),
        (str(3), 'Other'),
        (str(4), 'Unknown'),)
    sex = models.CharField(max_length = 24, choices=sex_list, verbose_name='Sex')
    address = models.TextField(max_length = 256)

    @property
    def full_name(self):
        #"Returns the person's full name."
        return '%s %s' % (self.firstname, self.lastname)

    #Redirects after form is submitted using primary key
    def get_absolute_url(self):
        return reverse('clincher:main_detail', kwargs={'pk': self.pk})

    def __str__(self):
        return self.firstname + ' ' + self.lastname +' - ' + str(self.date_of_birth)




class Visit(models.Model):
    fk_visit_main = models.ForeignKey(Main, on_delete=models.CASCADE, verbose_name=('Patient Name'))
    visit_date = models.DateField(auto_now = True, editable=True)
    fk_visit_user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name=('Practitioner'), max_length=500)
    visit_label = models.CharField(max_length=256, blank=True, null=True)
    visit_type = models.CharField(
        max_length=256,
        default=1, verbose_name='Type of Visit')
    visit_progress_notes = models.TextField(max_length=10000,
                                   blank=True, verbose_name='Progress Notes')
    outcomes = models.BooleanField(default=False)

    def __str__(self):
        return '%s %s' % (self.visit_date, self.visit_label)

    def get_absolute_url(self):
        return reverse('clincher:main_detail', kwargs={'pk': self.pk})

forms.py

class VisitForm(forms.Form):
    visit_types_list = (
        (str(1), 'Consultation'),
        (str(2), 'Procedure'),
        (str(3), 'Administrative'),)
    visit_type = forms.ChoiceField(choices=visit_types_list)
    visit_label = forms.CharField(label='Visit Label', max_length=100)
    progress_note = forms.CharField(widget=forms.Textarea)

    def form_valid(self, form):
        form.instance.fk_visit_user = self.request.user
        form.instance.fk_visit_main = Main.objects.get(id=self.kwargs['pk'])
        return super().form_valid(form)

因此,我应该以带有父对象主键的子记录/对象结尾。

以上代码可以正常工作,但是我敢肯定,有一种简单的Django-ey方法可以使工作更简单,更可靠。我认为应该在Django RelationshipManager中找到我的解决方案,但找不到有效的解决方案。我在Fiver上付了一个钱,我认为他没有把这个变得尽可能简单。

2 个答案:

答案 0 :(得分:2)

检查django InlineFormset:https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#inline-formsets

如果您需要负担添加一个删除表单集的动态检出(基于Jquery): https://github.com/elo80ka/django-dynamic-formset

如果您使用的是基于类的视图,则必须在get_context_data()form_valid()内添加inlineformset,检查formset.is_valid()是否存在,然后将其保存到数据库中。

编辑:这是基于您的评论的代码

forms.py

class VisitForm(forms.ModelForm);
    class Meta:
        model = Visit
        fields = [
            'visit_type',
            'visit_label',
            'visit_progress_notes'
        ]

views.py

class CreateVisitView(CreateView):
    model = Visit
    form_class = VisitForm
    template_name =  "clincher/visit_form.html"

    #one of the first function called in class based view, best place to manage conditional access
    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponseForbidden()
        return super(CreateVisitView,self).dispatch(request, *args, **kwargs)

    def form_valid(self, form):
        visit = form.save(commit=False)
        visit.fk_visit_user = self.request.username
        visit.fk_visit_main = get_object_or_404(Main, pk=self.kwargs.get('pk'))
        visit.save()

        return super(CreateVisitView,self).form_valid(form)

models.py

    class Main(models.Model):
        SEX_LIST_CHOICE = (
            (str(1), 'Female'),
            (str(2), 'Male'),
            (str(3), 'Other'),
            (str(4), 'Unknown'),
        )

        firstname = models.CharField(max_length = 256, verbose_name=('First Name'))
        middlename = models.CharField(max_length=256, verbose_name=('Middle Name'))
        lastname = models.CharField(max_length=256, verbose_name=('Last Name'))
        date_of_birth = models.DateField()

        age = models.PositiveSmallIntegerField()

        sex = models.CharField(max_length = 24, choices=SEX_LIST_CHOICE, verbose_name='Sex')
        address = models.TextField(max_length = 256)

        @property
        def full_name(self):
            #"Returns the person's full name."
            return '%s %s' % (self.firstname, self.lastname)

        #Redirects after form is submitted using primary key
        def get_absolute_url(self):
            return reverse('clincher:main_detail', kwargs={'pk': self.pk})

        def __str__(self):
            return self.firstname + ' ' + self.lastname +' - ' + str(self.date_of_birth)



class Visit(models.Model):
    VISIT_TYPE_CHOICE = (
        (str(1), 'Consultation'),
        (str(2), 'Procedure'),
        (str(3), 'Administrative'),)

    fk_visit_main = models.ForeignKey(Main, on_delete=models.CASCADE, verbose_name=('Patient Name'))
    visit_date = models.DateField(auto_now = True, editable=True)
    fk_visit_user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name=('Practitioner'), max_length=500)
    visit_label = models.CharField(max_length=256, blank=True, null=True)
    #you are storing the type of visit as an 
    visit_type = models.CharField(
        max_length=256,
        default=1,
        verbose_name='Type of Visit',
        choices=VISIT_TYPE_CHOICE
    )
    visit_progress_notes = models.TextField(max_length=10000,
                                   blank=True, verbose_name='Progress Notes')
    outcomes = models.BooleanField(default=False)

    def __str__(self):
        return '%s %s' % (self.visit_date, self.visit_label)

    def get_absolute_url(self):
        return reverse('clincher:main_detail', kwargs={'pk': self.pk})

答案 1 :(得分:1)

因此,您可以清除其中的许多内容。

  1. instance.middelname = form.cleaned_data['middlename']永远不会起作用,因为实例名上的中间名不正确。

  2. 您可以使用Main.objects.create(firstname=form.validated_data['firstname'], lastname= .... etc)创建模型实例

  3. 您应该应该通过Main而不是Visit来将User与模型之间的关系。这将使您可以更轻松地添加“拜访”记录,例如,员工记录拜访记录而不是客户。

  4. 您应该查找CreateView来帮助您创建实例。

  5. 重命名主模型。到底是什么对我来说,它看起来像个人资料,但称其为Main并不是很好的描述。

  6. 年龄应为整数字段。没有人是“戴夫”岁。