Search code examples
pythondjangoadminmodels

How can I use all objects in a table as a label - input in a model in Django's Admin?


I want to know if there's some easy way to have 2 models, for example Language, and Word. And we have another model Translation that has the string of the translation and both Models are referenced with a Foreign Key.

Imagine I have 2 languages, English and Spanish. Is there some way to make always appear every language as a label and the string of the translation as a textbox?


Solution

  • If I correctly understand your issue, I think you should look at inlines.

    For instance, let's say you have the following models.

    class Language(models.Model):
        name = models.CharField()
    
    class Word(models.Model):
        value = models.CharField()
        translations = models.ManyToMany(Language, through='Translation')
    
    class Translation(models.Model):
        language = models.ForeignKey(Language)
        word = models.ForeignKey(Word)
        value = models.CharField()
    

    Then you could have this in your admin.py:

    class TranslationInline(admin.TabularInline):
        model = Translation
        readonly_fields = ['language']
    
        def get_max_num(self, *args, **kwargs):
            return Language.objects.all().count()
    
        def get_min_num(self, *args, **kwargs):
            return self.get_max_num(*args, **kwargs)
    
        def get_initial(self, request):
            return [
                {'language': l}
                for l in Language.objects.exclude(pk__in=self.get_queryset(request))
            ]
    
    @admin.register(Word)
    class WordAdmin(admin.ModelAdmin):
        inlines = [TranslationInline]
    
        def _create_formsets(self, request, obj, change):
            formsets = []
            inline_instances = []
            prefixes = {}
            get_formsets_args = [request]
            if change:
                get_formsets_args.append(obj)
            for FormSet, inline in self.get_formsets_with_inlines(*get_formsets_args):
                prefix = FormSet.get_default_prefix()
                prefixes[prefix] = prefixes.get(prefix, 0) + 1
                if prefixes[prefix] != 1 or not prefix:
                    prefix = "%s-%s" % (prefix, prefixes[prefix])
                formset_params = {
                    'instance': obj,
                    'prefix': prefix,
                    'queryset': inline.get_queryset(request),
                }
                if hasattr(inline, 'get_initial'):
                    formset_params['initial'] = inline.get_initial(request)
                if request.method == 'POST':
                    formset_params.update({
                        'data': request.POST,
                        'files': request.FILES,
                        'save_as_new': '_saveasnew' in request.POST
                    })
                formsets.append(FormSet(**formset_params))
                inline_instances.append(inline)
            return formsets, inline_instances
    

    This will enable you to add/edit translations of a word in the admin.