Как следить за 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
по-прежнему не работает. Я собираюсь провести расследование, поскольку эта функция прекрасно работает из веб-интерфейса.