This article is the third day of Django Advent Calendar 2019. (Advent calendar) is the first post.
I'm a non-engineer who has been making apps personally with Django for the past month or so. When I used a foreign key in that app, when I opened the pull-down, more than 100 items were displayed, so I wondered if I could squeeze it somehow.
To conclude first, you can use limit_choices_to
to filter the records displayed by foreign keys.
Let's actually create an app and try it out to see how to use it.
I made a simple app for explanation. Only Model. By the way, the theme is an application that registers the prediction of which team the players who acquired the FA right in 2019 will go to. It's almost decided, so it's not practical! https://github.com/shimayu22/fa_expects_app/
In the FaExpects table, the Players table is set as a foreign key so that registered players can select it from the pull-down menu. Player data is also available, so you can try it just by reading it. Please refer to README for how to use it.
https://github.com/shimayu22/fa_expects_app/tree/part1
python manage.py run server
http://127.0.0.1:8000/admin/fa_expects/faexpects/add/
When you open the "Player" pull-down menu,
It will be displayed in a sloppy manner like the image.Is it possible to filter on the management screen?
From ForeignKey.limit_choices_to in the Django documentation
Set limits on the choices available in this field when this field is rendered using ModelForm or admin (by default, all objects in the query set can be selected). You can use either a dictionary, a Q object, or a callable object that returns a dictionary or Q object. (Google Translate)
So you can use limit_choices_to
to filter the selectable items.
https://github.com/shimayu22/fa_expects_app/tree/part2
Added limit_choices_to to player_id of FaExpects.
models.py
player_id = models.ForeignKey(
Players,
on_delete=models.CASCADE,
verbose_name="player",
limit_choices_to={"position":1,}
)
This time, only the position of "1" (pitcher) can be selected. with this, http://127.0.0.1:8000/admin/fa_expects/faexpects/add/ When you open the "Player" pull-down menu,
Only the players registered as pitchers were displayed!
In the above example, you can narrow down by equality such as position == 1
, but by using the search keyword, you can narrow down by using the above and the following.
models.py
player_id = models.ForeignKey(
Players,
on_delete=models.CASCADE,
verbose_name="player",
limit_choices_to={"position": 1,
"age__lt": 33}
)
The above stipulates position == 1 and age <33
(pitcher and under 33 years old).
If you add __lt
(two underscores) to the item name ʻage`, the condition" less than "is added.
There are fewer players to choose from! The following articles are very well organized for other keywords.
Reference: [Summary of Django database operations #List of search keywords](https://qiita.com/okoppe8/items/66a8747cf179a538355b#%E6%A4%9C%E7%B4%A2%E3%82%AD%E3 % 83% BC% E3% 83% AF% E3% 83% BC% E3% 83% 89% E3% 81% AE% E4% B8% 80% E8% A6% A7)
Until now, search conditions were specified in dictionary type, but it is also possible to specify in Q object.
models.py
from django.db.models import Q
~~~abridgement~~~
player_id = models.ForeignKey(
Players,
on_delete=models.CASCADE,
verbose_name="player",
limit_choices_to=Q(position=4) | Q(position=7),
)
For Q objects, you can specify OR conditions like this. In the above, it means "the position is second baseman or outfielder".
Only the second baseman or the outfielder could be displayed brilliantly!
https://github.com/shimayu22/fa_expects_app/tree/part3
If it is solid writing, it is not flexible, so I will create a dictionary type with a function and pass it.
This time, in order to make it crisp, I made a table for specifying conditions.
I am adding the RequestedConditions
class to Models.py
.
(Because it is long, please check Models.py)
Then I created a function to create a dictionary for limit_choices_to from the latest record in the RequestedConditions
table.
Models.py
~~~abridgement~~~
def set_players_condition():
condition = RequestedConditions.objects.latest('pk')
condition_dict = {}
if condition.age > 0:
condition_dict["age__lt"] = condition.age
if condition.position > 0:
condition_dict["position"] = condition.position
if condition.dominant_hand > 0:
condition_dict["dominant_hand"] = condition.dominant_hand
return condition_dict
~~~abridgement~~~
First, http://127.0.0.1:8000/admin/fa_expects/requestedconditions/add/ Set "age", "position", and "dominant hand" and save. (If you do not set all, all will be displayed)
After registration http://127.0.0.1:8000/admin/fa_expects/faexpects/add/ If you look at the players in, only the players who meet the conditions will be displayed.
I did it.
If you just want to do something with the model, you can do it like this. It seems that you can make it more flexible by combining Q objects. Please play around with it and try it out.
It may be natural if you think calmly,
limit_choices_to={"age__lt":RequestedConditions.objects.latest('pk').age}
If you write something like this, you will get an error saying "There is no such table" when you first migrate.
Sora (when I try to reference a table that hasn't been created yet) Yes (I get an error) (Isn't it natural?).
Summary of Django database operations
Also, the following articles were helpful for Django in general. Thank you! [Python] Django Tutorial-Creating a General Purpose Business Web App Fastest [Django] Model Field Settings Template
Recommended Posts