Как следить за HTTP-перенаправлением?

У меня есть 2 разных вида, которые, кажется, работают сами по себе. Но когда я пытаюсь использовать их вместе с http-переадресацией, это не удается.

Контекст довольно прост, у меня есть представление, которое создает объект, и другое представление, которое обновляет этот объект, оба с одной и той же формой.

Единственное, что немного отличается, - это то, что мы используем несколько сайтов. Поэтому мы проверяем, является ли сайт, который хочет обновить объект, сайтом, который его создал. Если да, то выполняется обычное обновление объекта. Если нет (это та часть, которая здесь не работает), то я http перенаправляю представление обновления на представление создания и передаю объект, чтобы новый сайт мог создать новый объект на основе этих начальных значений.

Вот тест на создание нового ресурса (успешно пройден) :

@pytest.mark.resource_create
@pytest.mark.django_db
def test_create_new_resource_and_redirect(client):
    data = {
        "title": "a title",
        "subtitle": "a sub title",
        "status": 0,
        "summary": "a summary",
        "tags": "#tag",
        "content": "this is some updated content",
    }

    with login(client, groups=["example_com_staff"]):
        response = client.post(reverse("resources-resource-create"), data=data)
    resource = models.Resource.on_site.all()[0]
    assert resource.content == data["content"]
    assert response.status_code == 302

Вот тест на создание нового ресурса из существующего объекта (успешно пройден) :

@pytest.mark.resource_create
@pytest.mark.django_db
def test_create_new_resource_from_pushed_resource_and_redirect(request, client):
    existing_resource = baker.make(models.Resource)

    other_site = baker.make(Site)
    existing_resource.site_origin = other_site
    existing_resource.sites.add(other_site)

    our_site = get_current_site(request)
    existing_resource.sites.add(our_site)

    original_content = "this is some original content"
    existing_resource.content = original_content

    existing_resource.save()

    data = {
        "title": "a title",
        "subtitle": "a sub title",
        "status": 0,
        "summary": "a summary",
        "tags": "#tag",
        "content": "this is some updated content",
    }

    url = reverse("resources-resource-create-from-shared", args=[existing_resource.id])
    with login(client, groups=["example_com_staff"]):
        response = client.post(url, data=data)

    assert response.status_code == 302

    existing_resource.refresh_from_db()
    assert existing_resource.content == original_content
    assert our_site not in existing_resource.sites.all()

    new_resource = models.Resource.on_site.get()
    assert new_resource.content == data["content"]

Вот вид создания :

@login_required
def resource_create(request, pushed_resource_id=None):
    """
    Create new resource

    In case of a resource that is pushed from a different site
    create a new resource based on the pushed one.
    """
    has_perm_or_403(request.user, "sites.manage_resources", request.site)

    try:
        pushed_resource = models.Resource.objects.get(id=pushed_resource_id)
        pushed_resource_as_dict = model_to_dict(pushed_resource)
        initial_data = pushed_resource_as_dict
    except ObjectDoesNotExist:
        pushed_resource = None
        initial_data = None

    if request.method == "POST":
        form = EditResourceForm(request.POST, initial=initial_data)
        if form.is_valid():
            resource = form.save(commit=False)
            resource.created_by = request.user
            with reversion.create_revision():
                reversion.set_user(request.user)
                resource.save()
                resource.sites.add(request.site)
                if pushed_resource:
                    pushed_resource.sites.remove(request.site)
                    pushed_resource.save()
                    resource.site_origin = request.site
                    resource.save()
                form.save_m2m()

            next_url = reverse("resources-resource-detail", args=[resource.id])
            return redirect(next_url)
    else:
        form = EditResourceForm()
    return render(request, "resources/resource/create.html", locals())

Вот тест для обновления ресурса с оригинального сайта (проходит успешно) :

@pytest.mark.resource_update
@pytest.mark.django_db
def test_update_resource_from_origin_site_and_redirect(request, client):
    resource = baker.make(models.Resource)
    our_site = get_current_site(request)
    resource.site_origin = our_site
    resource.save()

    previous_update = resource.updated_on

    url = reverse("resources-resource-update", args=[resource.id])

    data = {
        "title": "a title",
        "subtitle": "a sub title",
        "status": 0,
        "summary": "a summary",
        "tags": "#tag",
        "content": "this is some updated content",
    }

    with login(client, groups=["example_com_staff"]):
        response = client.post(url, data=data)

    assert response.status_code == 302

    resource.refresh_from_db()
    assert resource.content == data["content"]
    assert resource.updated_on > previous_update

И, наконец, тест на обновление с другого сайта, который должен создать новый ресурс из исходного (этот тест не работает):

@pytest.mark.resource_update
@pytest.mark.django_db
def test_update_resource_from_non_origin_site_and_redirect(request, client):
    original_resource = baker.make(models.Resource)
    our_site = get_current_site(request)
    other_site = baker.make(Site)
    original_resource.sites.add(our_site, other_site)
    original_resource.site_origin = other_site
    previous_update = original_resource.updated_on
    original_content = "this is some original content"
    original_resource.content = original_content

    original_resource.save()

    assert models.Resource.on_site.all().count() == 1

    url = reverse("resources-resource-update", args=[original_resource.id])

    updated_data = {
        "title": "a title",
        "subtitle": "a sub title",
        "status": 0,
        "summary": "a summary",
        "tags": "#tag",
        "content": "this is some updated content",
    }

    with login(client, groups=["example_com_staff"]):
        response = client.post(url, data=updated_data)

    assert response.status_code == 302

    original_resource.refresh_from_db()
    assert original_resource.content == original_content
    assert original_resource.updated_on == previous_update
    assert other_site in original_resource.sites.all()
    assert our_site not in original_resource.sites.all()

    assert models.Resource.on_site.all().count() == 1
    new_resource = models.Resource.on_site.get()
    assert new_resource.content == updated_data["content"]
    assert other_site not in new_resource.sites.all()
    assert our_site in new_resource.sites.all()

Что происходит: здесь не создается новый объект, а вместо него модифицируется исходный объект.

Вот вид обновления :

@login_required
def resource_update(request, resource_id=None):
    """Update informations for resource"""
    has_perm_or_403(request.user, "sites.manage_resources", request.site)

    resource = get_object_or_404(models.Resource, pk=resource_id)

    if resource.site_origin is not None and resource.site_origin != request.site:
        pushed_resource_id = resource.id
        next_url = reverse("resources-resource-create-from-shared",
                           args=[pushed_resource_id]
                           )
        return redirect(next_url)

    next_url = reverse("resources-resource-detail", args=[resource.id])
    if request.method == "POST":
        form = EditResourceForm(request.POST, instance=resource)
        if form.is_valid():
            resource = form.save(commit=False)
            resource.updated_on = timezone.now()

            with reversion.create_revision():
                reversion.set_user(request.user)
                resource.save()
                form.save_m2m()

            return redirect(next_url)
    else:
        form = EditResourceForm(instance=resource)
    return render(request, "resources/resource/update.html", locals())

А модельная форма :

class EditResourceForm(forms.ModelForm):
    """Create and update form for resources"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Queryset needs to be here since on_site is dynamic and form is read too soon
        self.fields["category"] = forms.ModelChoiceField(
            queryset=models.Category.on_site.all(),
            empty_label="(Aucune)",
            required=False,
        )

        self.fields["contacts"] = forms.ModelMultipleChoiceField(
            queryset=addressbook_models.Contact.on_site.all(),
            required=False,
        )

        # Try to load the Markdown template into 'content' field
        try:
            tmpl = get_template(
                template_name="resources/resource/create_md_template.md"
            )
            self.fields["content"].initial = tmpl.render()
        except TemplateDoesNotExist:
            pass

    content = MarkdownxFormField(label="Contenu")

    title = forms.CharField(
        label="Titre", widget=forms.TextInput(attrs={"class": "form-control"})
    )
    subtitle = forms.CharField(
        label="Sous-Titre",
        widget=forms.TextInput(attrs={"class": "form-control"}),
        required=False,
    )
    summary = forms.CharField(
        label="Résumé bref",
        widget=forms.Textarea(
            attrs={"class": "form-control", "rows": "3", "maxlength": 400}
        ),
        required=False,
    )

    class Meta:
        model = models.Resource
        fields = [
            "title",
            "status",
            "subtitle",
            "summary",
            "tags",
            "category",
            "departments",
            "content",
            "contacts",
            "expires_on",
        ]

Любая идея о том, что я сделал не так, приветствуется. А если вы считаете, что следует применить лучшую стратегию, то не стесняйтесь комментировать.

Моя ошибка. Я использую initial=initial_data в POST части create представления. Что не имеет смысла.

При перемещении initial=initial_data в часть GET все работает.

Тест test_update_resource_from_non_origin_site_and_redirect по-прежнему не работает. Я собираюсь провести расследование, поскольку эта функция прекрасно работает из веб-интерфейса.

Вернуться на верх