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
Вернуться на верх