Django-nonrel form field for ListField

前端 未结 4 1678
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-08 09:21

I\'m experimenting with django-nonrel on appengine and trying to use a djangotoolbox.fields.ListField to implement a many-to-many relation. As I re

相关标签:
4条回答
  • 2020-12-08 09:29

    You could avoid a custom form class for such usage by inquiring for the model object

    class ModelListField(ListField):
      def __init__(self, embedded_model=None, *args, **kwargs):
      super(ModelListField, self).__init__(*args, **kwargs)
      self._model = embedded_model.embedded_model
    
      def formfield(self, **kwargs):
        return FormListField(model=self._model, **kwargs)
    
    class ListFieldWidget(SelectMultiple):
      pass
    
    class FormListField(MultipleChoiceField):
      widget = ListFieldWidget
    
      def __init__(self, model=None, *args, **kwargs):
        self._model = model
        super(FormListField, self).__init__(*args, **kwargs)
        self.widget.choices = [(unicode(i.pk), i) for i in self._model.objects.all()]
    
      def to_python(self, value):
        return [self._model.objects.get(pk=key) for key in value]
    
      def clean(self, value):
        return value
    
    0 讨论(0)
  • 2020-12-08 09:30

    OK, here is what I did to get this all working ... I'll start from the beginning

    This is what what my model looked like

    class MyClass(models.Model):
        field = ListField(models.ForeignKey(AnotherClass))
    

    I wanted to be able to use the admin interface to create/edit instances of this model using a multiple select widget for the list field. Therefore, I created some custom classes as follows

    class ModelListField(ListField):
        def formfield(self, **kwargs):
            return FormListField(**kwargs)
    
    class ListFieldWidget(SelectMultiple):
        pass
    
    class FormListField(MultipleChoiceField):
        """
        This is a custom form field that can display a ModelListField as a Multiple Select GUI element.
        """
        widget = ListFieldWidget
    
        def clean(self, value):
            #TODO: clean your data in whatever way is correct in your case and return cleaned data instead of just the value
            return value
    

    These classes allow the listfield to be used in the admin. Then I created a form to use in the admin site

    class MyClassForm(ModelForm):
        def __init__(self, *args, **kwargs):
            super(MyClasstForm,self).__init__(*args, **kwargs)
            self.fields['field'].widget.choices = [(i.pk, i) for i in AnotherClass.objects.all()]
            if self.instance.pk:
                self.fields['field'].initial = self.instance.field
    
        class Meta:
            model = MyClass
    

    After having done this I created a admin model and registered it with the admin site

    class MyClassAdmin(admin.ModelAdmin):
        form = MyClassForm
    
        def __init__(self, model, admin_site):
            super(MyClassAdmin,self).__init__(model, admin_site)
    
    admin.site.register(MyClass, MyClassAdmin)
    

    This is now working in my code. Keep in mind that this approach might not at all be well suited for google_appengine as I am not very adept at how it works and it might create inefficient queries an such.

    0 讨论(0)
  • 2020-12-08 09:30

    As far as I understand, you're trying to have a M2M relationship in django-nonrel, which is not an out-of-the-box functionality. For starters, if you want a quick hack, you can go with this simple class and use a CharField to enter foreign keys manually:

    class ListFormField(forms.Field):
        """ A form field for being able to display a djangotoolbox.fields.ListField. """
    
        widget = ListWidget
    
        def clean(self, value):
            return [v.strip() for v in value.split(',') if len(v.strip()) > 0]
    

    But if you want to have a multiple selection from a list of models normally you'd have to use ModelMultipleChoiceField, which is also not functional in django-nonrel. Here's what I've done to emulate a M2M relationship using a MultipleSelectField:

    Let's say you have a M2M relationship between 2 classes, SomeClass and AnotherClass respectively. You want to select the relationship on the form for SomeClass. Also I assume you want to hold the references as a ListField in SomeClass. (Naturally you want to create M2M relationships as they're explained here, to prevent exploding indexes if you're working on App Engine).

    So you have your models like:

    class SomeClass(models.Model):
        another_class_ids = ListField(models.PositiveIntegerField(), null=True, blank=True)
        #fields go here
    
    class AnotherClass(models.Model):
        #fields go here
    

    And in your form:

    class SomeClassForm(forms.ModelForm):
    
        #Empty field, will be populated after form is initialized
        #Otherwise selection list is not refreshed after new entities are created.
        another_class = forms.MultipleChoiceField(required=False)
    
    def __init__(self, *args, **kwargs):
        super(SomeClassForm,self).__init__(*args, **kwargs)
        self.fields['another_class'].choices = [(item.pk,item) for item in AnotherClass.objects.all()]
    
        if self.instance.pk: #If class is saved, highlight the instances that are related
            self.fields['another_class'].initial = self.instance.another_class_ids
    
    def save(self, *args, **kwargs):  
        self.instance.another_class_ids = self.cleaned_data['another_class']         
        return super(SomeClassForm, self).save()
    
    class Meta:
        model = SomeClass
    

    Hopefully this should get you going for the start, I implemented this functionality for normal forms, adjust it for admin panel shouldn't be that hard.

    0 讨论(0)
  • 2020-12-08 09:54

    This could be unrelated but for the admin interface, be sure you have djangotoolbox listed after django.contrib.admin in the settings.. INSTALLED_APPS

    0 讨论(0)
提交回复
热议问题