简体   繁体   中英

Django : several models returns in queryset

Yeah I know, it's not possible. Or maybe I didn't see. But, I'm gonna explain why I need this. Let's do some dummy classes:

class A(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)  
    object_id = models.PositiveIntegerField()
    lvl_struct = GenericForeignKey('content_type', 'object_id')

Let's say A can be attached to a struct (logical struct, like department in jobs). An A instance can be attached to only one struct, but there're 4 disctinct type of struct, and that's where I discovered generic foreign key (instead of a polymorphism on A).

But now, my problem is, in my form, I want to attach the actual struct when I create a A instance :

class CreateAForm(forms.ModelForm)
   lvl_struct = forms.ModelChoiceField(                                                        
       queryset=None, #here is my problem 
       required=True
   ) 

So here I would like to have a unique select with all possibilities (all instances of first struct type, all instances of second struct type and so on). So is there any way to do this, or will I have to do like four select with some js to check at least one and only one select has a value? Or of course a third solution which I didn't see. Tell me.

Thank you in advance for your time.

For this task I am using ChoiceField instead of ModelChoiceField. Convert all the querysets of your objects into combined list inside of form init, then create list with display names and assign it to choices like:

class MyForm(forms.ModelForm):

    content_object = forms.ChoiceField(label=_('Object'), widget=forms.Select(required=True)

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)

        # combine object_type and object_id into a single 'generic_obj' field
        # getall the objects that we want the user to be able to choose from
        available_objects = list(ModelOne.objects.filter(...))
        available_objects += list(ModelTwo.objects.filter(...))
        available_objects += list(ModelThree.objects.filter(...))

        # now create our list of choices for the <select> field
        object_choices = []
        for obj in available_objects:
            type_id = ContentType.objects.get_for_model(obj.__class__).id
            obj_id = obj.id
            form_value = "type:%s-id:%s" % (type_id, obj_id)  # e.g."type:12-id:3"
            display_text = str(obj)
            object_choices.append([form_value, display_text])

        self.fields['content_object'].choices = object_choices

When you save the form you have to decode the string to get chosen content_type and object_id. After that you can assign them back to instance. You can do it with:

from django.contrib.contenttypes.models import ContentType

def save(self, *args, **kwargs):

    # get object_type and object_id values from combined generic_obj field
    object_string = self.cleaned_data['content_object']
    matches = re.match("type:(\d+)-id:(\d+)", object_string).groups()

    object_type_id = matches[0]  # get 45 from "type:45-id:38"
    object_id = matches[1]  # get 38 from "type:45-id:38"

    object_type = ContentType.objects.get(id=object_type_id)

    self.instance.object_id = object_id
    self.instance.content_type = object_type

    super(MyForm, self).save(*args, **kwargs)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM