Skip to content

Commit

Permalink
fix(chant create): move folio-sequence uniqueness check to Form obj
Browse files Browse the repository at this point in the history
Moves validity checks on the Chant Create view to the Chant Create Form.

Refs: #1713.
  • Loading branch information
dchiller committed Dec 3, 2024
1 parent 00c8088 commit d69a155
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 56 deletions.
29 changes: 18 additions & 11 deletions django/cantusdb_project/main_app/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ class Meta:
"incipit_of_refrain",
"later_addition",
"rubrics",
"source",
]
# the widgets dictionary is ignored for a model field with a non-empty
# choices attribute. In this case, you must override the form field to
Expand Down Expand Up @@ -239,17 +240,23 @@ class Meta:
help_text="Select the project (if any) that the chant belongs to.",
)

# automatically computed fields
# source and incipit are mandatory fields in model,
# but have to be optional in the form, otherwise the field validation won't pass
source = forms.ModelChoiceField(
queryset=Source.objects.all().order_by("title"),
required=False,
error_messages={
"invalid_choice": "This source does not exist, please switch to a different source."
},
)
incipit = forms.CharField(required=False)
def clean(self) -> dict[str, Any]:
"""
Provide custom clean method that ensures the created chant does
not duplicate the folio and c_sequence of an already-existing chant.
"""
# Call super().clean() to ensure that the form's built-in validation
# is run before our custom validation.
super().clean()
folio = self.cleaned_data["folio"]
c_sequence = self.cleaned_data["c_sequence"]
source = self.cleaned_data["source"]
if source.chant_set.filter(folio=folio, c_sequence=c_sequence):
raise forms.ValidationError(
"Chant with the same sequence and folio already exists in this source.",
code="duplicate-folio-sequence",
)
return self.cleaned_data


class SourceCreateForm(forms.ModelForm):
Expand Down
69 changes: 24 additions & 45 deletions django/cantusdb_project/main_app/views/chant.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,8 +880,16 @@ def test_func(self):

return user_can_edit_chants_in_source(user, self.source)

# if success_url and get_success_url not specified, will direct to chant detail page
def get_success_url(self):
"""
Get the incipit of the created chant (generated by a signal)
and display a success message.
"""
self.object.refresh_from_db()
messages.success(
self.request,
"Chant '" + self.object.incipit + "' created successfully!",
)
return reverse("chant-create", args=[self.source.id])

def get_initial(self):
Expand Down Expand Up @@ -960,53 +968,24 @@ def get_context_data(self, **kwargs: Any) -> dict[Any, Any]:
context["suggested_chants"] = suggested_chants
return context

def form_valid(self, form):
def get_form_kwargs(self):
"""
Validates the new chant.
Custom validation steps are:
- Check if a chant with the same sequence and folio already exists in the source.
- Compute the chant incipit.
- Adds the "created_by" and "updated_by" fields to the chant.
In the case of a submitted form (there is data in the request),
we copy the data dictionary and add the source id to it.
"""
# compute source
form.instance.source = self.source

# compute incipit, within 30 charactors, keep words complete
words = form.instance.manuscript_full_text_std_spelling.split(" ")
incipit = ""
for word in words:
new_incipit = incipit + word + " "
if len(new_incipit) >= 30:
break
incipit = new_incipit

form.instance.incipit = incipit.strip(" ")

# if a chant with the same sequence and folio already exists in the source
if (
Chant.objects.all()
.filter(
source=self.source,
folio=form.instance.folio,
c_sequence=form.instance.c_sequence,
)
.exists()
):
form.add_error(
None,
"Chant with the same sequence and folio already exists in this source.",
)
kwargs = super().get_form_kwargs()
if "data" in kwargs:
kwargs["data"] = kwargs["data"].copy()
kwargs["data"]["source"] = self.source.id
return kwargs

if form.is_valid():
form.instance.created_by = self.request.user
form.instance.last_updated_by = self.request.user
messages.success(
self.request,
"Chant '" + form.instance.incipit + "' created successfully!",
)
return super().form_valid(form)
return super().form_invalid(form)
def form_valid(self, form):
"""
Adds the "created_by" and "updated_by" fields to the chant.
"""
form.instance.created_by = self.request.user
form.instance.last_updated_by = self.request.user
return super().form_valid(form)


class ChantDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
Expand Down

0 comments on commit d69a155

Please sign in to comment.