djangoformview

Django FormView: Add if record does not exist and update if it does exist


I created a FormView and it works fine if the user executed the process the first time. However when it is executed the second time I get an error that the record already exist. This is expected as the user in the model is unique. How can I overcome this problem so that the current record is overwritten by the form.save if the record already exist.

models.py

class ttemp_selection(models.Model):
    select_account = models.ForeignKey(tledger_account, on_delete=models.CASCADE)
    date_from = models.DateField(default=datetime.today)
    date_to = models.DateField(default=datetime.today)
    user = models.ForeignKey(custom_user, on_delete=models.CASCADE, unique=True)

    def __str__(self):
        return self.select_account

forms.py

class Meta:
    model = ttemp_selection
    fields = ['select_account', 'date_from', 'date_to', 'user']

def __init__(self, *args, **kwargs):
    self.request = kwargs.pop('request')
    super(SelectAccountForm, self).__init__(*args, **kwargs)
    user = self.request.user
    current_company = user.current_company
    self.fields['select_account'].queryset = tledger_account.objects.filter(
        company=current_company, gl_category='Cash and Bank')

view.py

class sasView(FormView):
    template_name = 'cashflow/select_account.html'
    form_class = SelectAccountForm
    success_url = 'home'
    def form_valid(self, form):
        form.save()
        return super().form_valid(form)

    def get_form_kwargs(self):

        kwargs = super(sasView, self).get_form_kwargs()
        kwargs['request'] = self.request
        return kwargs
    

I can determine the record by using ttemp_selection.objects.get(user=request.user) I know I can make use of the UpdateView class but that will create a problem when the record does not exist. It will also add an extra step that is unnecessary. Assistance will be appreciated.


Solution

  • You can work with a CreateView, and slightly alter the behavior to specify a self.object if that exists:

    from django.contrib.auth.mixins import LoginRequiredMixin
    
    class sasView(LoginRequiredMixin, CreateView):
        template_name = 'cashflow/select_account.html'
        form_class = SelectAccountForm
        success_url = 'home'
        
        def get_form(self, *args, **kwargs):
            self.object = ttemp_selection.objects.filter(
                user=self.request.user
            ).first()
            return super().get_form(*args, **kwargs)
    
        def form_valid(self, form):
            form.instance.user = self.request.user
            return super().form_valid(form)
    
        def get_form_kwargs(self):
            kwargs = super(sasView, self).get_form_kwargs()
            kwargs['request'] = self.request
            return kwargs

    It however makes no sense to include the user as field, since - if I understand it correctly - you use the logged in user. By including it, you make it possible that a person forges a POST request, and thus changes the account of a different user. You should omit this filed:

    class SelectAccountForm(forms.ModelForm):
        class Meta:
            model = ttemp_selection
            #                                        no user ↓
            fields = ['select_account', 'date_from', 'date_to']
        
        # …

    Note: You can limit views to a class-based view to authenticated users with the LoginRequiredMixin mixin [Django-doc].