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:
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:
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:
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:
{% 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 %}