Django:如何为非外键字段实现ForeignKeyRawIdWidget效果?
Got it, let's break down how to add that popup-style raw ID selector for your Slum model's name field in a custom form. The ForeignKeyRawIdWidget is built for foreign keys, but we can roll our own custom widget based on Django's core RawIdWidget to replicate that exact behavior for non-foreign-key fields. Here's a step-by-step solution:
1. Ensure your Slum model is registered in the admin
The popup selector relies on the admin's changelist page for Slum entries. If you haven't already, register the model so the popup has a page to pull from:
from django.contrib import admin from .models import Slum @admin.register(Slum) class SlumAdmin(admin.ModelAdmin): list_display = ('name', 'id') # Include name so it's visible in the popup selection
2. Create a custom widget extending RawIdWidget
We'll override key methods to make the widget target your name field, display the selected name instead of just an ID, and link to the correct admin popup:
from django.contrib.admin.widgets import RawIdWidget from django.contrib import admin from django.urls import reverse from .models import Slum class SlumNameRawIdWidget(RawIdWidget): def __init__(self, *args, **kwargs): self.model = Slum # For Django <2.0, replace `remote_field` with `rel` super().__init__(rel=self.model._meta.pk.remote_field, *args, **kwargs) def label_for_value(self, value): # Show the selected Slum's name next to the input (instead of just ID) if value: try: slum = self.model.objects.get(pk=value) return slum.name except self.model.DoesNotExist: return "" return "" def get_url(self): # Point the popup to the Slum admin changelist with popup mode enabled return reverse(f"admin:{self.model._meta.app_label}_{self.model._meta.model_name}_changelist") + "?_popup=1"
3. Use the custom widget in your custom form
Since you're using a non-ModelForm, define your field with the new widget. We'll add a clean method to convert the selected Slum ID (stored by the widget) to the actual name value your form needs:
from django import forms from .widgets import SlumNameRawIdWidget class YourCustomForm(forms.Form): slum_name = forms.CharField(widget=SlumNameRawIdWidget(admin_site=admin.site)) def clean_slum_name(self): # Convert the selected Slum ID to its name for form submission slum_id = self.cleaned_data.get("slum_name") if slum_id: try: slum = Slum.objects.get(pk=slum_id) return slum.name except Slum.DoesNotExist: raise forms.ValidationError("Invalid slum selected.") return ""
4. Optional: Styling tweaks
The widget will render with the same magnifying glass icon and layout as the default ForeignKeyRawIdWidget. If you need to adjust the look, you can override the widget's render method or add custom CSS, but the default should match Django's admin styling out of the box.
内容的提问来源于stack exchange,提问作者Abhijit Ghate

