์๋
,
OR ์กฐ๊ฑด์ผ๋ก ์ฌ๋ฌ ํ๋๋ฅผ ํํฐ๋งํ๋ ๋ฐฉ๋ฒ!?
์๋ฅผ ๋ค์ด:
first_name = foo OR last_name = bar
๋๋ฑ:
model.objects.filter(Q(first_name=foo) | Q(last_name=bar))
์ด์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ์ด๊ฒ์ ์ ๊ฐ ๊ฐ์ง ์ง๋ฌธ๊ณผ ์ ํํ ๊ฐ์ต๋๋ค.
ํ์ฌ ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ๊ณ ๊ฐ ํ์๋ฉ๋๋ค.
/usr/local/lib/python3.7/site-packages/django_filters/rest_framework/backends.py:128:
UserWarning: <class 'project.api.views.FooBarViewSet'> is not compatible with schema generation
๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ FilterSet๊ณผ ViewSet์ด ์์ต๋๋ค.
# models
class FooBarUserAssignment(models.Model):
user = models.ForeignKey("auth.User", on_delete=models.CASCADE)
foobar = models.ForeignKey("FooBar", on_delete=models.CASCADE)
class Meta:
unique_together = (
("user", "foobar"),
)
class FooBarGroupAssignment(models.Model):
group = models.ForeignKey("auth.Group", on_delete=models.CASCADE)
foobar = models.ForeignKey("FooBar", on_delete=models.CASCADE)
class Meta:
unique_together = (
("group", "foobar"),
)
class FooBar(models.Model):
title = models.CharField(max_length=160, unique=True)
users = models.ManyToManyField(
to="auth.User",
through=FooBarUserAssignment,
)
groups = models.ManyToManyField(
to="auth.Group",
through=FooBarGroupAssignment,
)
def __str__(self):
return self.title
# filters
from django_filters import rest_framework as rest_framework_filters
class FooBarFilter(rest_framework_filters.FilterSet):
title = rest_framework_filters.CharFilter(field_name="title", lookup_expr="icontains")
class Meta:
model = FooBar
fields = ("title", )
# viewsets
class FooBarViewSet(ModelViewSet):
queryset = Foobar.objects.order_by("-title")
serializer_class = FooBarSerializer
filterset_class = FooBarFilter
def get_queryset(self):
queryset = self.queryset
q_name = Q()
rel_name = self.request.query_params.get("rel_name", None)
if rel_name:
q_name = Q(users__name=rel_name)
q_groups = Q()
rel_groups = self.request.query_params.get("rel_groups", "").split(",")
if any(rel_groups):
q_groups = Q(groups__name__in=rel_groups)
qs = queryset.filter(q_name | q_groups).distinct()
return qs
์ ํํ ๋์ผํ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ธฐ ์ํด ์ด๋ป๊ฒ FilterSet์ ๋ง๋ค ์ ์์ต๋๊น?
FilterSet์ ์ผ๋ถ๋ฅผ ๊ตฌํํ ์ ์์์ง๋ง OR
๋
ผ๋ฆฌ๋ฅผ ViewSet.get_queryset()
์์ FilterSet
๋ก ์ด๋ํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ด ๋ฌด์์ธ์ง ์ฌ์ ํ ๋ชจ๋ฆ
๋๋ค.
import django_filters
from django_filters import rest_framework as rest_framework_filters
class CharInFilter(django_filters.BaseInFilter, rest_framework_filters.CharFilter):
pass
class FooBarFilter(rest_framework_filters.FilterSet):
title = rest_framework_filters.CharFilter(field_name="title", lookup_expr="icontains")
rel_name = rest_framework_filters.CharFilter(field_name="users__name", lookup_expr="exact")
rel_groups = CharInFilter(field_name="groups__name")
class Meta:
model = FooBar
fields = ("title", )
def filter_queryset(self, queryset):
qs = super().filter_queryset(queryset)
return qs.distinct()
์ด๊ฒ์ ์ฌ์ด๋ django-filter์ ํจ๊ป ์ค์นํ ์ ์๋ django-filter ํ์ฅ์ ์ผ๋ถ์ธ ๊ฒ ๊ฐ์ต๋๋ค. https://github.com/philipn/django-rest-framework-filters#complex -operations
์๋ ๋ชจ๋. ๊ทธ๋ฃน ์์ค ์ ํจ์ฑ ๊ฒ์ฌ ๋ฐ ํํฐ๋ง์ ์ ๊ณตํด์ผ ํ๋ #1167์ ๋ง๋ค์์ต๋๋ค. OP์ ๊ฒฝ์ฐ ์ฌ์ฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
class UserFilter(FilterSet):
class Meta:
model = User
field = ['username', 'first_name', 'last_name']
groups = [
CombinedGroup(filters=['first_name', 'last_name'], combine=operator.or_),
]
PR์ ๋ํ ํผ๋๋ฐฑ์ ํฌ๊ฒ ๊ฐ์ฌํ๊ฒ ์ต๋๋ค.
์ฌ์ ํ ๋์ผํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ์์ง ์๋ฃจ์ ์ด ์์ต๋๊น?
@JeromeK13 ์ ์๋ฌด๊ฒ๋ ์๋ ๊ฒ ๊ฐ์ต๋๋ค! ๊ฐ์ ์ง๋ฌธ์ด ์์ต๋๋ค
๋ค์๊ณผ ๊ฐ์ ์์ ์ ์ํํ์ง๋ง ๋ชจ๋ ๊ฒฝ์ฐ์ ํจ๊ณผ๊ฐ ์๋์ง ํ์คํ์ง ์์ง๋ง ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค.
class BaseFilter(django_filters.FilterSet):
def OR(self, queryset, field_name, value):
if not hasattr(self, "groups"):
setattr(self, "groups", {})
self.groups[field_name] = value
return queryset
<strong i="6">@property</strong>
def qs(self):
base_queryset = super().qs
if not hasattr(self, "groups"):
return base_queryset
query = Q()
for key, value in self.groups.items():
query |= Q(**{key: value})
return base_queryset.filter(query)
class PlanFilter(BaseFilter):
double_visit__or = django_filters.UUIDFilter("double_visit", method="OR")
visitor__or = django_filters.UUIDFilter("visitor", method="OR")
class Meta:
model = models.Plan
fields = {
"id": ["exact"],
"date": ["exact", "gte", "lte",],
"visitor": ["exact"],
"doctor": ["exact"],
"double_visit": ["exact"]
}
ํจ์ฌ ๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ์ฐพ์์ต๋๋ค.
from django.db.models.query_utils import Q
user_contacts = Contact.objects.order_by('-contact_date').filter(Q(sender_id=request.user.user_id)|Q(receiver_id=request.user.user_id))
์ ๊ฒฝ์ฐ์๋ sender_id
๋๋ receiver_id
๊ฐ ์๋ ๋ชจ๋ ํ์ ์์งํ๊ณ ์ถ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ date
๋ก ์ฃผ๋ฌธํ๋๋ก ์ค์ ๋ ํ๋์ ์ฟผ๋ฆฌ์ ํฌํจ๋๊ธฐ๋ฅผ ์ํ์ต๋๋ค.
์๋ ,
OR ์กฐ๊ฑด์ผ๋ก ์ฌ๋ฌ ํ๋๋ฅผ ํํฐ๋งํ๋ ๋ฐฉ๋ฒ!?
์๋ฅผ ๋ค์ด:
first_name = foo OR last_name = bar
๋๋ฑ:
model.objects.filter(Q(first_name=foo) | Q(last_name=bar))
django.db.models ๊ฐ์ ธ์ค๊ธฐ Q
๊ทธ๊ฒ์ ๋๋ฅผ ์ํด ์ผํ์ต๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
๋ค์๊ณผ ๊ฐ์ ์์ ์ ์ํํ์ง๋ง ๋ชจ๋ ๊ฒฝ์ฐ์ ํจ๊ณผ๊ฐ ์๋์ง ํ์คํ์ง ์์ง๋ง ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค.