简体   繁体   中英

Getting Foreign Key data in Django Admin Add/Change Form

I am trying to customise the Django admin add/change for a project. I have created a model called "Visit" which contains 3 Foreign Key fields: "Customer", "Pet" and "Doctor". The workflow is as follows:

  • The user creates a customer.
  • The user creates a pet and associates it with a customer.
  • The user creates a visit and associates it with a pet and a doctor.

Below is the code for my models.py

class Visit(models.Model):
    customer = models.ForeignKey('customer.Customer', on_delete=models.CASCADE)
    pet = models.ForeignKey('pet.Pet', on_delete=models.CASCADE)
    date = models.DateTimeField()
    doctor = models.ForeignKey(
        'configuration.Doctor', on_delete=models.DO_NOTHING, null=True, blank=True)
    status = models.CharField(
        choices=PET_STATUS, max_length=3, null=True, blank=True)
    reason = models.CharField(max_length=255)
    diagnosis = models.TextField(null=True, blank=True)
    treatment = models.TextField(null=True, blank=True)
    comment = models.TextField(null=True, blank=True)
    prescription = models.TextField(null=True, blank=True)
    weight = models.DecimalField(
        max_digits=6, decimal_places=2, null=True, blank=True)

    class Meta:
        ordering = ('-date',)

My issue is that someone using the Django Admin to create a Visit can wrongly choose a Customer and Pet. Hence, the Customer does not own that Pet. I would like to know how can I customise the Django Admin, so that, when the user selects a Customer, only Pets under that particular Customer is displayed in the selectbox.

Below is my admin.py

class VisitAdmin(admin.ModelAdmin):
    change_form_template = 'visit/invoice_button.html'
    add_form_template = 'visit/add_visit.html'

    list_display = ('customer', 'pet', 'date', 'status')
    list_filter = ('date', 'customer', 'pet', 'status')
    search_fields = ('customer__first_name',
                     'customer__last_name', 'pet__pet_name')
    autocomplete_fields = ['customer', 'pet', 'doctor']
    radio_fields = {'status': admin.HORIZONTAL}
    fieldsets = (
        (None, {
            "fields": ('customer', 'pet', 'doctor'),
        }),
        ("Visit Details", {
            "fields": ('date', 'reason', 'weight', 'status'),
        }),
        ("Visit Outcome", {
            "fields": ('diagnosis', 'treatment', 'comment')
        })
    )
    inlines = [FeeInLine, AttachmentInLine]

    actions = ['export_as_csv']

    def export_as_csv(self, request, queryset):

        meta = self.model._meta
        field_names = [field.name for field in meta.fields]

        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename={}.csv'.format(
            meta)
        writer = csv.writer(response)

        writer.writerow(field_names)
        for obj in queryset:
            row = writer.writerow([getattr(obj, field)
                                   for field in field_names])

        return response

    export_as_csv.short_description = "Export Selected"

    def response_change(self, request, obj):
        if "invoice" in request.POST:
            return render_pdf(request, obj.pk)
        return super().response_change(request, obj)


admin.site.register(Visit, VisitAdmin)

i faced the same issue, in my case i use raw_id_field for my foreign keys and many to many fields in ModelAdmin And override add and change template.

you can use raw_id_field for forign keys and in your templates write javascript to change href of search icon for Pet foreign key field when Customer id field changed, and in href use url lookup to show only Pet which belongs to selected Customer

# stock/models.py

class Category(models.Model):
    title = models.CharField(max_length=255, blank=True)
    is_active = models.BooleanField(default=True)


class Product(models.Model):

    category = models.ForeignKey(
        Category, on_delete=models.PROTECT, related_name="product"
    )

    feature_option = models.ManyToManyField("FeatureOption", blank=True, related_name="product")


class Feature(models.Model):
    title = models.CharField(max_length=255, blank=True)
    category = models.ManyToManyField(Category, blank=True, related_name="feature")


class FeatureOption(models.Model):
    title = models.CharField(max_length=255, blank=True)
    feature = models.ForeignKey(
        Feature, on_delete=models.CASCADE, related_name="feature_option"
    )


# stock/admin.py

class CategoryAdmin(admin.ModelAdmin):
    raw_id_fields = ['parent']


class ProductAdmin(admin.ModelAdmin):
    add_form_template = 'admin/product_add_form.html'
    change_form_template = 'admin/product_change_form.html'
    raw_id_fields = ['category', "feature_option"]


class FeatureOptionAdmin(admin.ModelAdmin):
    list_filter = (("feature__category", admin.RelatedOnlyFieldListFilter),)

and in my template i use javascript to change the href of FeatureOption search icon for url lookup

<!-- product_add_form.html -->

{% extends "admin/change_form.html" %}
{% load i18n %}


{% block admin_change_form_document_ready %} {{ block.super }}
    <script lang="text/javascript">
        function changeFeatureOptionPopupUrl() {
            const featureOptionBaseUrl = "/admin/stock/featureoption/";
            let categoryTextBox = document.getElementById("id_category");
            document.getElementById("lookup_id_feature_option").href = featureOptionBaseUrl + "?feature__category__id__exact=" + categoryTextBox.value;
        }
        document.getElementById("lookup_id_feature_option").onfocus = function () {changeFeatureOptionPopupUrl()}
    </script>
{% endblock %}

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