# Django modelformset with modelform that has excluded fields with unique together but it is not working

**URL:** <https://forum.djangoproject.com/t/django-modelformset-with-modelform-that-has-excluded-fields-with-unique-together-but-it-is-not-working/27570>\
**Category:** Forms & APIs\
**Created:** [January 30, 2024, 11:16am UTC](https://forum.djangoproject.com/t/django-modelformset-with-modelform-that-has-excluded-fields-with-unique-together-but-it-is-not-working/27570 "2024-01-30T11:16:42Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![darync2](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/darync2/32/18839_2.png) [@darync2](https://forum.djangoproject.com/u/darync2)\
**Post date:** [January 30, 2024, 11:16am UTC](https://forum.djangoproject.com/t/django-modelformset-with-modelform-that-has-excluded-fields-with-unique-together-but-it-is-not-working/27570/1 "2024-01-30T11:16:42Z")

</div>

I have a form where a user selects a player and a position and submits it, however there is a unique together on the model that says a player, user and draft session is unique. This does not work as the fields are not in the form as they are set manually later, this is not for the user to select. I tried looking into custom form validation but was unable to find a solution there, I also checked into full clean in the instances but I am not able to add to the form errors and return to the form. Any advise on how to overcome this?

I have the following models:

```auto
class DuringSeasonPlayerDraft(models.Model):
    start_datetime = models.DateTimeField()
    end_datetime = models.DateTimeField()
    owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
    open = models.BooleanField(default=False)

    class Meta:
        ordering = ["open", "start_datetime"]

class DuringSeasonPlayerPick(models.Model):
    during_season_draft = models.ForeignKey(DuringSeasonPlayerDraft, on_delete=models.CASCADE)
    player = models.ForeignKey(Player, on_delete=models.CASCADE)
    position = models.ForeignKey(PlayerPosition, on_delete=models.SET_NULL, null=True, blank=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
    timestamp = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["player"]
        unique_together = [["during_season_draft", "player", "user"]]

    def unique_error_message(self, model_class, unique_check):
        if model_class == type(self) and unique_check == ("during_season_draft", "player", "user"):
            return "You cannot have duplicate players"
        else:
            return super(DuringSeasonPlayerPick, self).unique_error_message(model_class, unique_check)

```

Forms:

```auto
class DuringSeasonPlayerForm(forms.ModelForm):
    def __init__ (self, *args, **kwargs):
        super(DuringSeasonPlayerForm, self). __init__ (*args, **kwargs)
        self.fields["player"] = forms.ModelChoiceField(queryset=Player.objects.filter(active=True))

    class Meta:
        model = DuringSeasonPlayerPick
        fields = ["player", "position"]

DuringSeasonPlayerPickFormset = modelformset_factory(DuringSeasonPlayerPick, form=DuringSeasonPlayerForm, extra=1, max_num=1, can_delete=True)

```

View:

```auto
def during_season_player_pick_list(request, pk):
    template_name = "fantasy_football/during_season_player_pick.html"
    
    draft = DuringSeasonPlayerDraft.objects.get(id=pk)
    picks = DuringSeasonPlayerPick.objects.filter(during_season_draft=draft, user=request.user)
    
    context = {}

    if request.method == "POST":
        player_picks_formset = DuringSeasonPlayerPickFormset(request.POST, request.FILES, queryset=picks)

        for form in player_picks_formset:
            if timezone.now() >= draft.end_datetime:
                form.add_error(None, "Cannot make changes after draft end datetime.")

        if player_picks_formset.is_valid():
            instances = player_picks_formset.save(commit=False)
            for i in instances:
                i.during_season_draft = draft
                i.user = request.user
                i.save()
            
            for i in player_picks_formset.deleted_objects:
                i.delete()
            
            return redirect("during_season_player_pick", pk=pk)

    elif request.method == "GET":
        player_picks_formset = DuringSeasonPlayerPickFormset(queryset=picks)
        
    context["player_picks_formset"] = player_picks_formset

    return render(request, template_name, context)

```

Template:

```auto
{% extends "base.html" %}

{% load widget_tweaks %}
{% load crispy_forms_tags %}

{% block content %}

<h6>During Season Player Picks</h6>
<form id="form-container" action="" method="post">
  {% csrf_token %}
  {{player_picks_formset|crispy }}
  <button id="add-form" type="button" class="btn btn-sm btn-secondary">Add Another Player</button>
  <button type="submit" class="btn btn-sm btn-primary">Save</button>
</form>
{% endblock %}

{% block javascript %}
<script>
  let playerForm = document.querySelectorAll(".multiField");
  let container = document.querySelector("#form-container");
  let addButton = document.querySelector("#add-form");
  let totalForms = document.querySelector("#id_form-TOTAL_FORMS");

  let formNum = playerForm.length-1;

  addButton.addEventListener('click', addForm);

  function addForm(e) {
    e.preventDefault()

    let newForm = playerForm[0].cloneNode(true);
    let formRegex = RegExp("form-(\\d){1}-","g");

    formNum++;
    newForm.innerHTML = newForm.innerHTML.replace(formRegex, `form-${formNum}-`);
    
    container.insertBefore(newForm, addButton);

    const player_select = document.querySelector(`#id_form-${formNum}-player`);
    const position_select = document.querySelector(`#id_form-${formNum}-position`);

    player_select.value = "";
    position_select.value = "";

    totalForms.setAttribute("value", `${formNum+1}`);
};
</script>
{% endblock %}

```

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 30, 2024, 12:02pm UTC](https://forum.djangoproject.com/t/django-modelformset-with-modelform-that-has-excluded-fields-with-unique-together-but-it-is-not-working/27570/2 "2024-01-30T12:02:55Z")

</div>

Welcome @darync2 !

When posting code, templates, or error messages here, please enclose the code (etc) between lines of three backtick - ` characters. This means you’ll have a line of ```, then your code, then another line of ```. This forces the forum software to keep your code properly formatted. (I’ve taken the liberty of editing your original post for this.)

---

<div class="post-metadata">

**Author:** ![darync2](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/darync2/32/18839_2.png) [@darync2](https://forum.djangoproject.com/u/darync2)\
**Post date:** [February 1, 2024, 1:27pm UTC](https://forum.djangoproject.com/t/django-modelformset-with-modelform-that-has-excluded-fields-with-unique-together-but-it-is-not-working/27570/3 "2024-02-01T13:27:08Z")

</div>

Hi Ken, do you have any advise on this scenario?

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [February 1, 2024, 3:16pm UTC](https://forum.djangoproject.com/t/django-modelformset-with-modelform-that-has-excluded-fields-with-unique-together-but-it-is-not-working/27570/4 "2024-02-01T15:16:34Z")

</div>

Well, you’re right. If those fields aren’t part of the form, then no form validation is going to occur using those fields.

I can think of a couple different possibilities - none that really jump out at me as being all that great - but seem to be workable.

- Add those fields to the form, as disabled and hidden. (That at least gets those fields into the form to be used during validation.)

- Store those values as extra attributes on the form, so that they would also be available during validation.

- Catch the error in the section of the view where you’re trying to save the data, and return the rendered view at that point.

There might be others options, but if there are, they’re not coming to my mind.

---

<div class="post-metadata">

**Author:** ![darync2](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/darync2/32/18839_2.png) [@darync2](https://forum.djangoproject.com/u/darync2)\
**Post date:** [February 7, 2024, 3:07pm UTC](https://forum.djangoproject.com/t/django-modelformset-with-modelform-that-has-excluded-fields-with-unique-together-but-it-is-not-working/27570/5 "2024-02-07T15:07:06Z")

</div>

> [@KenWhitesell](#):
>
> Catch the error in the section of the view where you’re trying to save the data, and return the rendered view at that point.

Hi Ken,

I’d like to investigate this option further, after the formset save (commit=False) is called, how do i add errors back to the forms/formset to render a view again with the errors? I was unable to figure this out.

Appreciate any assistance on this.

Thanks

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [February 7, 2024, 3:23pm UTC](https://forum.djangoproject.com/t/django-modelformset-with-modelform-that-has-excluded-fields-with-unique-together-but-it-is-not-working/27570/6 "2024-02-07T15:23:42Z")

</div>

It’s this block of code that I think would need to be modified:

> [@darync2](#):
>
> ```auto
> for i in instances:
> i.during_season_draft = draft
> i.user = request.user
> i.save()
> 
> ```

First, I’d assume you’d want to wrap this up in a transaction, such that when an entry failed, they’d all fail. This way, all entries would be rejected if there’s an error. (Or, you _could_ try to save them all and just keep track of the forms with errors. A little more work, but possibly more useful.)

For you to have reached this point, I _think_ [`formset.errors`](https://docs.djangoproject.com/en/5.0/topics/forms/formsets/#formset-validation) would need to be empty. That means you can create a new list to be assigned to `formset.errors`. You could even build it incrementally as you’re iterating over the individual forms.

Then, once you’re done, you _may_ be able to call `is_valid` again on the formset. If it’s false, you return the same value at this point in the code as if the original `is_valid` test failed instead of returning the redirect.

---

<div class="post-metadata">

**Author:** ![darync2](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/darync2/32/18839_2.png) [@darync2](https://forum.djangoproject.com/u/darync2)\
**Post date:** [February 7, 2024, 4:52pm UTC](https://forum.djangoproject.com/t/django-modelformset-with-modelform-that-has-excluded-fields-with-unique-together-but-it-is-not-working/27570/7 "2024-02-07T16:52:16Z")

</div>

Hi Ken,

You are correct, this is where I think the code should be added for the form errors. I would like to add the error on the object to the respective form so that in the form the errors shows with the problem form. However since I am iterating over the instances which are model objects and not the form itself where I would set the error, I am having trouble wrapping me head around the correct way to iterate the form and the instances together so that I can line them up so to speak

Thanks

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [February 7, 2024, 7:31pm UTC](https://forum.djangoproject.com/t/django-modelformset-with-modelform-that-has-excluded-fields-with-unique-together-but-it-is-not-working/27570/8 "2024-02-07T19:31:06Z")

</div>

> [@darync2](#):
>
> correct way to iterate the form and the instances together

You don’t need to specifically iterate over the forms.  
What I’m thinking of should be able to be achieved something like this (pseudo code here, not actual code):

```auto
formset.errors = []
for i in instances:
   ... stuff ...
   is this an error?
        formset.errors.append({'field name': 'Error message'})
   else:
        formset.errors.append({})
if formset.is_valid():
    return redirect(...)
return render(...)
```
