Search code examples
djangodjango-modelsdjango-admin

Django Admin search field on foreignkey doesn't work


I am building a django project and try to get a search bar on a "main" field which is called "regnr" (see models & admin modules below) but I get ani contains error when I search. I have tried search_fields = [foreignkey__name] and also [name_id] and also. [name] but nothing seems to work...

from django.db import models

# Create your models here.
class Fordonsregister(models.Model):
    regnr = models.CharField(max_length=6, primary_key=True, null=False, blank=False)
    namn = models.CharField(max_length=255, null=True, blank=True)
    tel = models.CharField(max_length=255, default='070-000 00 00', null=True, blank=True)
    email = models.EmailField(null=True, blank=True)
    kund_sedan = models.DateTimeField(auto_now=True)
    kund_points = models.IntegerField(default=0)

    def __str__(self) -> str:
        return self.regnr

    class Meta:
        ordering = ['regnr']

class ServiceTypes(models.Model):
    typ = models.CharField(max_length=255, primary_key=True, blank=False)
    pris = models.DecimalField(decimal_places=2, max_digits=6, null=False, blank=False)
    beskrivning = models.CharField(max_length=255, null=True, blank=True)

    def __str__(self) -> str:
        return self.typ

    class Meta:
        ordering = ['typ']


class FordonsService(models.Model):
    regnr = models.ForeignKey(Fordonsregister, on_delete=models.CASCADE)
    typ = models.ForeignKey(ServiceTypes, on_delete=models.CASCADE)
    service_datum = models.DateTimeField()
    bokat_datum = models.DateTimeField(auto_now=True)
    kommentar = models.CharField(max_length=1000)

    class Meta:
        ordering = ['-service_datum']

And here is the admin module: The problem is with class FordonsServiceAdmin(admin.ModelAdmin):

from django.contrib import admin
from django.db.models import Count
from django.utils.html import format_html, urlencode
from django.urls import reverse
from django.http import HttpRequest
from .models import Fordonsregister, ServiceTypes, FordonsService

# Register your models here.
@admin.register(Fordonsregister)
class FordonsregisterAdmin(admin.ModelAdmin):
    list_display = ['regnr', 'namn', 'tel', 'email', 'antal_bokningar', 'antal_offerter']
    search_fields = ['regnr__istartswith']

    def antal_bokningar(self, fordonsregister):
        url = (
        reverse('admin:main_fordonsservice_changelist')
        + '?'
        + urlencode({
            'fordonregister__regnr': str(fordonsregister.regnr)
        }))
        return format_html('<a href="{}">{}</a>', url, fordonsregister.antal_bokningar)

    def antal_offerter(self, fordonsregister):
        url = (
        reverse('admin:kalkylator_standardoffert_changelist')
        + '?'
        + urlencode({
            'fordonsregister__regnr': str(fordonsregister.regnr)
        }))
        return format_html('<a href="{}">{}</a>', url, fordonsregister.antal_offerter)

    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        queryset = queryset.annotate(antal_bokningar=Count("fordonsservice__service_datum"), 
                                     antal_offerter=Count("standardoffert__totalpris"))
        return queryset




@admin.register(FordonsService)
class FordonsServiceAdmin(admin.ModelAdmin):
    list_display = ['regnr', 'typ', 'service_datum', 'bokat_datum', 'regnr_id']
    search_fields = ['fordonsregister__regnr_id']


@admin.register(ServiceTypes)
class ServiceTypesAdmin(admin.ModelAdmin):
    list_display = ['typ', 'pris', 'antal_bokat']

    def antal_bokat(self, servicetypes):
        url = (
        reverse('admin:main_fordonsservice_changelist')
        + '?'
        + urlencode({
            'servicetypes__typ': str(servicetypes.typ)
        }))
        return format_html('<a href="{}">{}</a>', url, servicetypes.antal_bokat)

    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        queryset = queryset.annotate(antal_bokat=Count("fordonsservice__service_datum"))
        return queryset

Solution

  • Your FordonsService model has a foreign key regnr that points to the Fordonsregister model, which in turn has another CharField named regnr (maybe you can be a bit descriptive while naming attributes -- it will be helpful for you as well as others that will be working on the code).

    Now, since you want to go from the foreign-key regnr and access any field on the Fordonsregister model, you do it via model__attribute syntax, like below:

    @admin.register(FordonsService)
    class FordonsServiceAdmin(admin.ModelAdmin):
        list_display = ['regnr', 'typ', 'service_datum', 'bokat_datum', 'regnr_id']
        search_fields = ['regnr__id', 'regnr__regnr']
    

    This will add a search for both the id and the regnr attributes of the Fordonsregister model, which is linked to the FordonsService model.