How can I follow an HTTP redirect?

I have 2 different views that seem to work on their own. But when I try to use them together with a http redirect then that fails.

The context is pretty straightforward, I have a view that creates an object and another view that updates this object, both with the same form.

The only thing that is a bit different is that we use multiple sites. So we check if the site that wants to update the object is the site that created it. If yes then it does a normal update of the object. If no (that’s the part that does not work here) then I http redirect the update view to the create view and I pass along the object so the new site can create a new object based on those initial values.

Here is the test to create a new resource (passes successfully) :

@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

Here is the test to create a new resource from an existing object (passes successfully) :

@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"]

Here is the create view :

@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())

Here is the test to update the resource from the original site (passes successfully) :

@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

And finally the test to update from a different site that should create a new resource from the original one (that one fails):

@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()

What happens is that no new object gets created here and the original object is modified instead.

Here is the update view :

@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())

And the model form :

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",
        ]

Any idea about what I did wrong is welcome. And if you think a better strategy should be employed then feel free to comment.

0

My bad. I use initial=initial_data in the POST part of the create view. Which makes no sense.

When moving the initial=initial_data to the GET part then it works.

The test_update_resource_from_non_origin_site_and_redirect test still fails though. I’m going to investigate since the feature works fine from within the web interface.

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật