2013-11-29 4 views
3

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

+1

Я понимаю, что они все внутри одного тега формы в html? Попробуйте придать каждой форме префикс, помогает избежать столкновений между именами полей. Https://docs.djangoproject.com/en/dev/ref/forms/api/#prefixes-for-forms – Anentropic

+0

Когда у меня есть множество форм в одном представлении , Я помещаю их в словарь, чтобы я мог перебирать их. Однако у вас есть особые требования к валидации (в отличие от проверки «is_valid» каждой формы), так что это может не сработать для вас. – leech

ответ

0

Пара вещей, которые я могу попытаться увидеть, если все работает. Сначала откройте оболочку и создайте некоторые формы и привяжите их к данным и проверите их вручную.Часто это поможет вам сделать еще один шаг в вашем расследовании.

Далее, всякий раз, когда вы работаете с несколькими формами и наборами форм, действуйте так, как @ Anentropic упоминает и использует префиксы. Я только что преодолел проблему, которая была прекрасно решена с использованием аргументов prefix и auto_id.

Похоже, что у вас сложный дизайн формы, поэтому, вероятно, вы сталкиваетесь с несколькими проблемами, и трудно понять, потому что у вас есть несколько движущихся частей.

Смежные вопросы