25👍
After hours of reading semi related questions I finally figured this out.
You can’t self reference a Model the way I was trying to do so there is no way to make Django act the way I wanted using limit_choices_to
because it can’t find the id of a different ForeignKey
in the same model.
This can apparently be done if you change the way Django works, but a simpler way to solve this was to make changes to admin.py instead.
Here is what this looks like in my models.py now:
# models.py
class MovieCategory(models.Model):
category = models.ForeignKey(Category)
movie = models.ForeignKey(Movie)
prefix = models.ForeignKey('Prefix', blank=True, null=True)
number = models.DecimalField(verbose_name='Movie Number', max_digits=2,
blank=True, null=True, decimal_places=0)
I simply removed limit_choices_to
entirely.
I found a similar problem here with the solution posted by Kyle Duncan. The difference though is that this uses ManyToMany
and not ForeignKey
. That means I had to remove filter_horizontal = ('prefix',)
under my class MovieCategoryAdmin(admin.ModelAdmin):
as that is only for ManyToMany
fields.
In admin.py I had to add from django import forms
at the top to create a form. This is how the form looks:
class MovieCategoryForm(forms.ModelForm):
class Meta:
model = MovieCategory
fields = ['prefix']
def __init__(self, *args, **kwargs):
super(MovieCategoryForm, self).__init__(*args, **kwargs)
self.fields['prefix'].queryset = Prefix.objects.filter(
category_id=self.instance.category.id)
And my AdminModel
:
class MovieCategoryAdmin(admin.ModelAdmin):
"""
Admin Class for 'Movie Category'.
"""
fieldsets = [
('Category', {'fields': ['category']}),
('Movie', {'fields': ['movie']}),
('Prefix', {'fields': ['prefix']}),
('Number', {'fields': ['number']}),
]
list_display = ('category', 'movie', 'prefix', 'number')
search_fields = ['category__category_name', 'movie__title', 'prefix__prefix']
form = MovieCategoryForm
This is exactly how Kyle describes it in his answer, except I had to add fields = ['prefix']
to the Form or it wouldn’t run. If you follow his steps and remember to remove filter_horizontal
and add the fields you’re using it should work.
Edit: This solution works fine when editing, but not when creating a new entry because it can’t search for the category id when one doesn’t exits. I am trying to figure out how to solve this.
8👍
Another approach, if you don’t want to add a custom ModelForm, is to handle this in your ModelAdmin’s get_form()
method. This was preferable for me because I needed easy access to the request object for my queryset.
class StoryAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(StoryAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['local_categories'].queryset = LocalStoryCategory.\
objects.filter(office=request.user.profile.office)
return form
- Django template indentation guideline
- Celery beat not picking up periodic tasks
- How to unit test methods inside django's class based views?
3👍
Keep in mind that limit_choices_to
supports "Either a dictionary, a Q object, or a callable returning a dictionary or Q object" and should theoretically support any lookup that can be done using django’s queryset filtering. A potential solution would then be filtering based on some property of the category that you control such as a slug field.
class MovieCategory(models.Model):
category = models.ForeignKey(Category)
movie = models.ForeignKey(Movie)
prefix = models.ForeignKey('Prefix', blank=True, null=True,
limit_choices_to=Q(category__slug__startswith='movie'))
number = models.DecimalField(verbose_name='Movie Number', max_digits=2,
blank=True, null=True, decimal_places=0)
2👍
I had the same question and your self-answer helped me get started. But I also found another post (question-12399803) that completed the answer, that is, how to filter when creating a new entry.
In views.py
form = CustomerForm(groupid=request.user.groups.first().id)
In forms.py
def __init__(self, *args, **kwargs):
if 'groupid' in kwargs:
groupid = kwargs.pop('groupid')
else:
groupid = None
super(CustomerForm, self).__init__(*args, **kwargs)
if not groupid:
groupid = self.instance.group.id
self.fields['address'].queryset = Address.objects.filter(group_id=groupid)
So, whether adding a new customer or updating an existing customer, I can click on a link to go add a new address that will be assigned to that customer.
This is my first answer on StackOverflow. I hope it helps.
- What is the difference between the create and perform_create methods in Django rest-auth
- How can I automatically let syncdb add a column (no full migration needed)
- Adding forgot-password feature to Django admin site
- Changing password in Django Admin
- How to download a filefield file in django view
0👍
Well, I’m not sure why are you doing this. But I think there’s easier way to set category for "MovieCategory" in views.py.
For example if you have urls:
path('category/<int:prefix_pk>/create', views.MovieCategoryCreateView.as_view(), name='movie_category_create'),
And then you can use prefix_pk in views.py:
def post(self, request, **kwargs):
prefix = get_object_or_404(Prefix, id=self.kwargs['prefix_pk'])
form = MovieCategoryForm(request.POST)
if not form.is_valid():
ctx = {'form': form, 'prefix ': prefix }
return render(request, self.template_name, ctx)
movie_category = form.save(commit=False)
movie_category.prefix = prefix
movie_category.save()
return redirect(reverse...)
- Adding forgot-password feature to Django admin site
- How to display "This many months ago" in Django using Humanize?
- TimeField format in Django template
- TypeError: create_superuser() missing 1 required positional argument: 'profile_picture'