Django - Как включить логику поля формы в modelForm, а не в Createview, чтобы форма могла быть нетестируемой без тестирования представления?
У меня есть Campaign
Модель, и CampaignCreateForm
которая является МодельюФормы. Модель Campaign имеет поле contact_list
типа JSONField. When a user is creating a campaign using the
CampaignCreateFormthey upload a CSV file which is processed to create the JSON data for the
поле "список_контактов".
Каким образом лучше всего подойти к этому, чтобы я мог тестировать форму отдельно от представления?
Я построил его, используя CampaignCreateView
, который наследуется от CreateView, и включил логику разбора CSV-файла и создания JSON-данных в метод views form_valid
, но это делает юнит-тестирование формы (и любой валидации полей формы) невозможным. Я хочу протестировать функции, включенные в метод forms clean
. При моем текущем подходе представление и форма должны тестироваться вместе, а это кажется неправильным.
Как я могу создать форму таким образом, чтобы вся логика для формы (обработка CSV файла для создания JSON данных и отбрасывание загруженного файла) обрабатывалась только в форме?
Мои текущие CreateView и ModelForm выглядят следующим образом:
Вид:
class CampaignCreateView(LoginRequiredMixin, CreateView):
model = Campaign
form_class = CampaignCreateForm # required if you want to use a custom model form, requires `model` also
template_name = "writing/campaign_create.html"
def get_success_url(self):
""" If model has get_absolute_url() defined, then success_url or get_success_url isnt neccessary
"""
user = User.objects.get(username=self.kwargs.get("username"))
return reverse("writing:campaigns", kwargs={"username": user.username})
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({"user": self.request.user})
return kwargs
def form_valid(self, form):
""" by default, form_valid redirects to success_url
"""
form.instance.user = self.request.user
form.instance.image_url = f"https://picsum.photos/seed/{randrange(10000)}/500/300"
file = form.cleaned_data["contact_list_file"]
file_content = file.open("r")
json_contact_list = csv_to_json(file_content)
form.instance.contact_list = json_contact_list
contact_list = json.loads(json_contact_list) # as python dict
form.instance.items = len(contact_list)
response = super().form_valid(form)
log_campaign_progress(pk=form.instance.pk, status="t2h-created", stage="campaign")
enqueue_handwriting_generation(campaign_pk=form.instance.pk)
return response
Форма:
class CampaignCreateForm(forms.ModelForm):
contact_list_file = forms.FileField(required=True)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop(
"user"
) # To get request.user. Do not use kwargs.pop('user', None) due to potential security hole
super().__init__(*args, **kwargs)
class Meta:
model = Campaign
fields = ("name", "message", "contact_list_file")
def clean(self):
# Cammpaign name is unique for the user
try:
Campaign.objects.get(name=self.cleaned_data["name"], user=self.user)
except Campaign.DoesNotExist:
pass
else:
self.add_error(
"name",
ValidationError(
_("You've already created a campaign with this name"), code="BadCampaignName"
),
)
# if an error is attached to a field then the field is removed from cleaned_data
self = check_message_length(self)
self = check_message_text_is_valid(self)
self = check_file_is_valid(self)
self = check_message_tags_exist_in_contact_list(self)
return self.cleaned_data
Модель:
# TimeStampedModel inherits form models.Model
class Campaign(TimeStampedModel):
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
)
order = models.ForeignKey(
Order,
on_delete=models.SET_NULL,
null=True,
)
name = models.CharField(max_length=80)
notes = models.CharField(max_length=2000, null=False, blank=True)
message = models.TextField(
max_length=1800,
null=False,
blank=False,
)
contact_list = models.JSONField(null=True)
items = models.PositiveIntegerField(null=False, blank=False)
purchased = models.BooleanField(default=False, null=False)
def __str__(self):
return self.name
class Meta:
ordering = ["-modified"]
unique_together = ("user", "name")
... more fields