Я работаю над задачей, имеющей 5 форм. Две из этих форм и другие генерируются с использованием modelformset_factory.Как управлять несколькими modelformset_factory на одной странице?
фон:
Мне нужно построить представление, которое обрабатывает эти 5 форм:
CdnUrl
|-- Http
|-- Location
| -- HttpRedirect
| -- HttpProxy
|-- Location
| -- HttpRedirect
| -- HttpProxy
|-- Location
| -- HttpRedirect
| -- HttpProxy
HttpProxy и HttpRedirect не может быть вставлен одновременно. Просто HttpRedirect или HttpProxy, не оба.
forms.py
class OneRequiredFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
super(OneRequiredFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
class CustomLocationFormset(OneRequiredFormSet):
def __init__(self, *args, **kwargs):
super(CustomLocationFormset, self).__init__(*args, **kwargs)
self.forms[0].fields["uri"].initial = "/"
self.forms[0].fields["uri"].widget.attrs["readonly"] = True
def clean(self):
super(CustomLocationFormset, self).clean()
if self.forms[0].cleaned_data["uri"] != "/":
raise forms.ValidationError(u"Your first URI must be '/'")
class CustomInlineStreamingFormset(BaseFormSet):
pass
class CdnUrlForm(forms.ModelForm):
class Meta:
model = CdnUrl
fields = ("account",)
def __init__(self, *args, **kwargs):
self.accounts_range = kwargs.pop("accounts_range", None)
super(CdnUrlForm, self).__init__(*args, **kwargs)
if self.accounts_range:
self.fields["account"].queryset = self.accounts_range
def save(self, commit=True, **kwargs):
service = kwargs.pop("service", None)
instance = super(CdnUrlForm, self).save(commit=False)
if service:
instance.service = service
if commit:
instance.save()
return instance
class HttpForm(forms.ModelForm):
CONFIGURATION_STATE_CHOICES = [
(True, "Enabled"),
(False, "Disabled")
]
GZIP_CHOICES = [
(True, "On"),
(False, "Off"),
]
configuration_state = forms.TypedChoiceField(choices=CONFIGURATION_STATE_CHOICES,
widget=forms.RadioSelect,
initial=True)
gzip = forms.TypedChoiceField(choices=GZIP_CHOICES,
widget=forms.RadioSelect,
initial=False)
class Meta:
model = Http
fields = ("cdnurl_allow_content_access",
"configuration_state",
"protocol_policy",
"ssl_certificate",
"gzip",
"cname_url",
"origin",
"host_header",
# admin fields
"manual_configuration",
"minimum_object_lifetime",
"connect_timeout",
"read_timeout",
"granularity_file_size",)
class LocationForm(forms.ModelForm):
class Meta:
model = Location
fields = ("uri",)
class HttpRedirectForm(forms.ModelForm):
class Meta:
model = HttpRedirect
fields = ("domain_redirect",)
class HttpProxyForm(forms.ModelForm):
CACHE_QUERY_STRING_CHOICES = [
(True, "Yes"),
(False, "No (Improves Caching)"),
]
cache_query_string = forms.TypedChoiceField(choices=CACHE_QUERY_STRING_CHOICES,
widget=forms.RadioSelect,
initial=False)
class Meta:
model = HttpProxy
fields = ("end_user_caching",
"expires_range",
"expires_value",
"cdn_object_caching",
"cdn_object_lifetime",
"allowed_http_methods",
"forward_cookie",
"white_list_cookie",
"cache_query_string",
"proxy_headers",
"remove_headers",
"comments",)
def clean(self):
cleaned_data = super(HttpProxyForm, self).clean()
expires_range = cleaned_data.get("expires_range")
expires_value = cleaned_data.get("expires_value")
if expires_range and expires_range.upper() == "Y" and expires_value > 10:
raise forms.ValidationError(u"Invalid Range (Max is 10 Years)")
return cleaned_data
Так что моя первоначальная идея заключается в том, что я должен иметь 2 формы и 3 modelformsets.
CdnUrlForm и HttpForm - это мои формы. LocationForm, HttpRedirectForm и HttpProxyForm будут использоваться для создания моих форм.
Я делаю это потому, чтобы сохранить последовательную запись, мне нужно: - данные CdnUrl - Http данные - Место данных - HttpProxy ИЛИ HttpRedirect данных
Мой дизайн:
CdnUrlForm (только одна форма) HttpForm (только одна форма) Местоположение (я могу вставить несколько местоположений на страницу) HttpProxy (я могу вставить только один HttpProxy для каждого местоположения) HttpRedirect (я могу вставить только один HttpRedirect для каждого места)
Вот мое мнение:
views.py
@reversion.create_revision()
@login_required
@user_passes_test(lambda u: u.is_active)
def create_configuration(request, service_type, service_code):
contact_logged = request.user.get_profile()
accounts_range = contact_logged.account.get_self_and_children()
contact_services = get_contact_services(request)
service = Services.objects.get(url_name=service_code)
cdn_url_form = CdnUrlForm(request.POST or None, accounts_range=accounts_range)
http_form = HttpForm(request.POST or None)
LocationFormSet = modelformset_factory(Location, form=LocationForm, formset=CustomLocationFormset, extra=2,
can_delete=False)
location_form_set = LocationFormSet(request.POST or None)
RedirectFormSet = modelformset_factory(HttpRedirect, form=HttpRedirectForm, can_delete=False, extra=2)
ProxyFormSet = modelformset_factory(HttpProxy, form=HttpProxyForm, can_delete=False, extra=2)
redirect_form_set = RedirectFormSet(request.POST or None, queryset=HttpRedirect.objects.none())
proxy_form_set = ProxyFormSet(request.POST or None, queryset=HttpProxy.objects.none())
if request.method == "POST":
if cdn_url_form.is_valid():
cdn_url_instance = cdn_url_form.save(commit=False, service=service)
if http_form.is_valid() and location_form_set.is_valid():
http_instance = http_form.save(commit=False)
for location_form in location_form_set:
location_instance = location_form.save(commit=False)
if redirect_form_set.is_valid():
for redirect_form in redirect_form_set:
cdn_url_instance.save()
redirect_instance = redirect_form.save(commit=False)
http_instance.cdn_url = cdn_url_instance
http_instance.save()
location_instance.http = http_instance
location_instance.save()
redirect_instance.location = location_instance
redirect_instance.save()
if proxy_form_set.is_valid():
for proxy_form in redirect_form_set:
cdn_url_instance.save()
proxy_instance = proxy_form.save(commit=False)
http_instance.cdn_url = cdn_url_instance
http_instance.save()
location_instance.http = http_instance
location_instance.save()
proxy_instance.location = location_instance
proxy_instance.save()
return HttpResponseRedirect(reverse("dashboard"))
return render_to_response(
"cdnsetup/configuration/create_or_edit.html",
{"contact_services": contact_services,
"service_type": service_type,
"cdn_url_form": cdn_url_form,
"http_form": http_form,
"location_form_set": location_form_set,
"redirect_form_set": redirect_form_set,
"proxy_form_set": proxy_form_set},
RequestContext(request),
)
Всякий раз, когда я отправляю данные этой точки зрения, это выглядит как мои modelformsets не проверяемые. Конечно, мой код неправильно, но я не знаю, как обрабатывать все эти формы, чтобы сделать следующее:
Вставка данных из CdnUrlForm Вставка данных из HttpForm Вставить все МЕСТОПОЛОЖЕНИЕ
Для каждого места, проверка если экземпляры HttpRedirectForm действительны. Если они действительны, я могу ТОЛЬКО вставлять данные HttpRedirectForm, а не HttpProxy (поэтому я бы игнорировал любые данные в HttpProxyForm).
Вот мои модели:
models.py
import uuid
from datetime import date
from django.conf import settings
from django.db import models
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from azionmanager.validators.urls_validator import validate_domain
class CdnUrl(models.Model):
cdn_url = models.CharField(_("cdn url"), max_length=255, null=False, blank=False, unique=True)
account = models.ForeignKey("controlpanel.Accounts", null=False, blank=False)
# related service
service = models.ForeignKey("controlpanel.Services", null=False, blank=False)
# sequential field used to create cdn url field
sequential = models.IntegerField()
@classmethod
def get_next_sequence(cls, account, service):
"""Get the next sequential number if CDN URL does not exist yet."""
sequential = cls.objects.filter(account=account, service=service).defer("sequential").order_by("-sequential")
if not sequential:
return 1
return sequential[0].sequential + 1
def create_cdn_url(self):
self.cdn_url = "{sequential}{account}.{service}.{domain}". \
format(sequential=self.sequential, account=self.account.client_id, service=self.service.url_name,
domain=settings.CDN_DOMAIN)
def get_absolute_url(self):
return reverse("cdnsetup.views.services_setup.edit_configuration",
args=[self.service.service_type.lower(),
self.service.url_name,
self.id])
def save(self, *args, **kwargs):
self.sequential = CdnUrl.get_next_sequence(self.account, self.service)
self.create_cdn_url()
super(CdnUrl, self).save(*args, **kwargs)
class Http(models.Model):
STATUS_CODES_CHOICES = [
("P", "Pending"),
("V", "Valid"),
("I", "Invalid"),
("L", "Locked")
]
PROTOCOL_CHOICES = [
("HTTP", "HTTP"),
("HTTPS", "HTTPS"),
("HTTP+HTTPS", "HTTP & HTTPS"),
]
GRANULARITY_FILE_SIZE_CHOICES = [
("SF", "Small Files"),
("LF", "Large Files"),
]
cdn_url = models.ForeignKey(CdnUrl)
# distribution settings
cdnurl_allow_content_access = models.BooleanField(_("allow content access through CDN url"), default=True)
configuration_state = models.BooleanField(_("configuration state"), default=True)
protocol_policy = models.CharField(_("viewer protocol policy"), max_length=10, choices=PROTOCOL_CHOICES, null=False,
blank=False,
default=PROTOCOL_CHOICES[0][0])
ssl_certificate = models.CharField(_("ssl certificate"), max_length=255, null=True, blank=True)
gzip = models.BooleanField(_("gzip content"), default=False)
cname_url = models.TextField(_("cname url"), max_length=255, null=False, blank=False)
# origin settings
origin = models.CharField(_("origin domain name"), validators=[validate_domain], max_length=255, null=False,
blank=False)
host_header = models.CharField(validators=[validate_domain], max_length=255, null=True, blank=True)
# used after configuration is saved
status = models.CharField(_("validation status"), max_length=1, choices=STATUS_CODES_CHOICES, editable=False,
default=STATUS_CODES_CHOICES[0][0])
# admin settings
manual_configuration = models.BooleanField(_("manual configuration"), default=False)
minimum_object_lifetime = models.IntegerField(_("minimum object lifetime"), default=60, null=False, blank=True)
connect_timeout = models.IntegerField(_("connect timeout"), default=60, null=True, blank=True)
read_timeout = models.IntegerField(_("read timeout"), default=120, null=True, blank=True)
granularity_file_size = models.CharField(_("granularity file size"), max_length=2,
choices=GRANULARITY_FILE_SIZE_CHOICES,
default=GRANULARITY_FILE_SIZE_CHOICES[1][0],
null=False, blank=True)
def __unicode__(self):
return "<{cdn_url}>: {cname_url}".format(cdn_url=self.cdn_url, cname_url=self.cname_url)
class Location(models.Model):
http = models.ForeignKey(Http)
uri = models.CharField(max_length=255, null=False, blank=False)
created_at = models.DateField(_("create date"), default=date.today)
updated_at = models.DateField(_("update date"), auto_now_add=True)
class HttpRedirect(models.Model):
location = models.ForeignKey(Location, related_name="redirect")
domain_redirect = models.URLField(_("domain redirect"), max_length=255, null=False, blank=False)
class HttpProxy(models.Model):
RANGE_UNITS = [
("S", "Seconds"),
("M", "Minutes"),
("H", "Hours"),
("D", "Days"),
("W", "Weeks"),
("M", "Months"),
("Y", "Years"),
]
GRANULARITY_FILE_SIZE = [
("SF", "Small Files"),
("LF", "Large Files"),
]
CACHING_CHOICES = [
("O", "Use Origin Cache Headers"),
("C", "Customize"),
]
location = models.ForeignKey(Location, related_name="proxy")
# cache settings
end_user_caching = models.CharField(_("end user caching"), max_length=1, choices=CACHING_CHOICES,
default=CACHING_CHOICES[0][0])
expires_range = models.CharField(_("expires_range"), max_length=2, choices=RANGE_UNITS, default=RANGE_UNITS[3][0])
expires_value = models.IntegerField(_("expires_value"), default=30)
cdn_object_caching = models.CharField(_("cdn object caching"), max_length=1, choices=CACHING_CHOICES,
default=CACHING_CHOICES[0][0])
cdn_object_lifetime = models.IntegerField(_("cdn object lifetime"), default=10080, null=False, blank=False)
allowed_http_methods = models.TextField(_("allowed http methods"), max_length=255, null=False, blank=False)
forward_cookie = models.CharField(_("forward cookie"), max_length=255, null=True, blank=True)
white_list_cookie = models.CharField(_("white list cookie"), max_length=255, null=True, blank=True)
cache_query_string = models.BooleanField(_("cache query string"), default=False)
proxy_headers = models.TextField(_("proxy headers"), null=True, blank=True)
remove_headers = models.TextField(_("remove headers"), null=True, blank=True)
# add comments to the configuration
comments = models.TextField(null=True, blank=True)
def clean(self):
if self.cdn_object_lifetime < self.location.http.cdn_url.minimum_object_lifetime:
raise ValidationError(u"Object lifetime can not be less than {minimum_object_lifetime}".format(
minimum_object_lifetime=self.location.http.cdn_url.minimum_object_lifetime))
def __unicode__(self):
return "<{cdn_url}>: {uri}".format(cdn_url=self.cdn_url, uri=self.uri)
Наконец, я не хочу Место экземпляры должны быть вставлены, если они не имеют HttpProxy или HttpRedirect экземпляров, присоединенные к ним. И, как я сказал ранее, у меня может быть несколько местоположений на странице и HttpProxy OR HttpRedirect, для которых одна из этих мест. Что мне нужно сделать, чтобы обрабатывать эти формы на одной странице?
Я понимаю, что они все внутри одного тега формы в html? Попробуйте придать каждой форме префикс, помогает избежать столкновений между именами полей. Https://docs.djangoproject.com/en/dev/ref/forms/api/#prefixes-for-forms – Anentropic
Когда у меня есть множество форм в одном представлении , Я помещаю их в словарь, чтобы я мог перебирать их. Однако у вас есть особые требования к валидации (в отличие от проверки «is_valid» каждой формы), так что это может не сработать для вас. – leech