Django-filter: рд░рд┐рд╡рд░реНрд╕ рдлреЙрд░реЗрди рдХреА рдХреЛ рдлрд╝рд┐рд▓реНрдЯрд░ рдирд╣реАрдВ рдХрд░ рд╕рдХрддрд╛

рдХреЛ рдирд┐рд░реНрдорд┐рдд 22 рдЬреВрди 2020  ┬╖  10рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: carltongibson/django-filter

рдкрд░рд┐рдЪрдп

рдирдорд╕реНрдХрд╛рд░! рдЗрд╕ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд-рдмрд╣реБрдд рдзрдиреНрдпрд╡рд╛рдж! рдореЗрд░реЗ рд╡рд░реНрддрдорд╛рди рдПрдкреАрдЖрдИ рдХреЗ рд╕рд╛рде рдЗрд╕рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдирд╛ рдмрд╣реБрдд рдЦреБрд╢реА рдХреА рдмрд╛рдд рд╣реИ!

рд╡рд░реНрддрдорд╛рди рд▓рдХреНрд╖реНрдп

рдореЗрд░реЗ рдкрд╛рд╕ рджреЛ рдореЙрдбрд▓ рд╣реИрдВ, рдПрдХ School рдФрд░ рдПрдХ SchoolCourse ред SchoolCourse рдХрд╛ рдХрдИ рд╕реЗ рдПрдХ рд╕рдВрдмрдВрдз School рдХреЗ рд╡рд┐рдкрд░реАрдд рд╣реИред

рдореИрдВ $#$ SchoolCourse $#$ рдХреЗ name рдХреЗ рдЖрдзрд╛рд░ рдкрд░ School рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ

рдЗрд╕рдХреЗ рдЕрддрд┐рд░рд┐рдХреНрдд рдПрдХ рдЦрд┐рдВрдЪрд╛рд╡ рд▓рдХреНрд╖реНрдп рдХреЗ рд░реВрдк рдореЗрдВ, рдореИрдВ рдЕрдВрддрддрдГ рднреА SchoolLesson рджреНрд╡рд╛рд░рд╛ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдкрд╕рдВрдж рдХрд░реВрдВрдЧрд╛ред

рдбреЗрдЯрд╛ рдореЙрдбрд▓

рд╕реНрдХреВрд▓ рдореЙрдбрд▓

class SchoolModel(Model):
    name = CharField(max_length=24, unique=True)

    REQUIRED_FIELDS = ["name"]

    class Meta:
        ordering = ("id",)

    def get_courses(self):
        school_courses = SchoolCourseModel.objects.filter(school=self)
        return school_courses

рд╕реНрдХреВрд▓ рдХреЛрд░реНрд╕

class SchoolCourseModel(Model):
    description = CharField(default="", max_length=200)
    name = CharField(max_length=50)
    school = ForeignKey(SchoolModel,
                        related_name="school_course",
                        on_delete=CASCADE)

    REQUIRED_FIELDS = ["school", "name"]

    class Meta:
        ordering = ("id",)
        unique_together = ("school", "name",)

    def get_lessons(self):
        school_lessons = SchoolLessonModel.objects.filter(course=self)
        return school_lessons

рдЬреЛ - рдпрджрд┐ рдЖрдк рдбрд┐рдмрдЧ рджреГрд╢реНрдп рдореЗрдВ рдЗрди рдореЙрдбрд▓реЛрдВ рдХреЗ рд╕рд╛рде рдЦреЗрд▓рддреЗ рд╣реИрдВ рддреЛ рдЖрдкрдХреЛ рдпрд╣ рджрд┐рдЦрд╛рдИ рджреЗрдЧрд╛

all_school_models[50].school_course
(Pdb++) <django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager object at 0x10a8177d0>
all_school_models[50].school_course.all()
(Pdb++) <QuerySet [<SchoolCourseModel: SchoolCourseModel object (151)>, <SchoolCourseModel: SchoolCourseModel object (152)>, <SchoolCourseModel: SchoolCourseModel object (153)>]>
all_school_models[50].school_course.all()[0]
(Pdb++) <SchoolCourseModel: SchoolCourseModel object (151)>
all_school_models[50].school_course.all()[0].name
(Pdb++) 'Nihongo Course One'

рдлрд╝рд┐рд▓реНрдЯрд░ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди

рдореИрдВрдиреЗ рдЬреЛ рдлрд╝рд┐рд▓реНрдЯрд░ рдмрдирд╛рдпрд╛ рд╣реИ рд╡рд╣ рдХрд╛рдлреА рд╕реАрдзрд╛-рд╕реАрдзрд╛ рд▓рдЧрддрд╛ рд╣реИред

class SchoolFilter(FilterSet):
    school_name = CharFilter(field_name="name",
                             lookup_expr="icontains")
    # ---
    course_name = CharFilter(field_name="school_course__name",
                             lookup_expr="icontains")
    course_description = CharFilter(field_name="school_course__description",
                                    lookup_expr="icontains")

    class Meta:
        model = SchoolModel
        fields = [
            "school_name",
            # ---
            "course_name",
            "course_description"
        ]

рдореБрд╕реАрдмрдд

рд╣рд╛рд▓рд╛рдВрдХрд┐ рдпрд╣ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред рдЕрдЧрд░ рдореИрдВ рдпрд╣ рдЕрдиреБрд░реЛрдз рдХрд░рддрд╛ рд╣реВрдВ:
{{API_URL}}/api/v1/schools/?course_name=Nihongo

рдореИрдВ рдЗрд╕реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рд╡рд╛рдкрд╕ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реВрдВ

{
    "schools": []
}

рдкреНрд░рд╢рди

рдореБрдЭрд╕реЗ рдпрд╣рд╛рдВ рдХреНрдпрд╛ рдЧрд▓рдд рд╣реЛ рд░рд╣рд╛ рд╣реИ?

рдХреНрдпрд╛ рдореИрдВ django-filters рдХреА рдЧрд▓рдд рд╡реНрдпрд╛рдЦреНрдпрд╛ рдХрд░ рд░рд╣рд╛ рд╣реВрдБ? рдХреНрдпрд╛ рдореИрдВ рдЧрд▓рдд рд╡реНрдпрд╛рдЦреНрдпрд╛ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рдХрд┐ рдбреЗрдЯрд╛ рдореЙрдбрд▓ рдХреЛ рдХреИрд╕реЗ рдЬреЛрдбрд╝рд╛ рдЬрд╛рдП?

рдХрд┐рд╕реА рднреА рдорд╛рд░реНрдЧрджрд░реНрд╢рди рдХреА рд╕рд░рд╛рд╣рдирд╛ рдХреА рдЬрд╛рддреА рд╣реИ!

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рдореЗрд░рд╛ рдЕрдиреБрдорд╛рди рд╣реИ рдХрд┐ рдпрд╣ рджрд┐рдП рдЧрдП рдСрдлрд╕реЗрдЯ рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рд╣реИред рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд┐рдП рдЧрдП рдХреНрд╡реЗрд░реАрд╕реЗрдЯ рдореЗрдВ рдХреЗрд╡рд▓ 1 рдСрдмреНрдЬреЗрдХреНрдЯ рд╣реИ, рд▓реЗрдХрд┐рди рдЕрдиреБрд░реЛрдз рдкреЗрдЬрд┐рдиреЗрдЯрд░ рдХреЛ рдкрд╣рд▓реЗ 49 рдЖрдЗрдЯрдо рдХреЛ рдЫреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣ рд░рд╣рд╛ рд╣реИред рдЕрдЧрд░ рдЖрдкрдХреЛ рдЬрд╛рдВрдЪрдирд╛ рдерд╛

print(filtered_school_models[49:])

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЖрдкрдХреЛ <QuerySet []> рдорд┐рд▓реЗрдЧрд╛ред

рдмрдВрдж рдХрд░рдирд╛, рдЬреИрд╕рд╛ рдХрд┐ рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рдкреЗрдЬрд┐рдиреЗрд╢рди рдореБрджреНрджрд╛ рд╣реИред

рд╕рднреА 10 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рд╣рд╛рдп @loganknechtред рдПрдХ рд╡рд┐рд╡реЗрдХ рдЬрд╛рдВрдЪ рдХреЗ рд░реВрдк рдореЗрдВ, рдХреНрдпрд╛ рдЖрдк рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдЖрдкрдХрд╛ URL рд╕рд╣реА рд╣реИ? рдЖрдкрдХреЗ рдЙрджрд╛рд╣рд░рдг URL рдореЗрдВ, рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ $#$ ? $#$ рдХреЗ рдмрдЬрд╛рдп / рд╕реЗ рд╢реБрд░реВ рд╣реЛрддреА рд╣реИред

рдЖрд╣ @rpkilby - рдХреНрд╖рдорд╛ рдХрд░реЗрдВ, рдпрд╣ Postman рд╕реЗ рдХреЙрдкреА рдФрд░ рдкреЗрд╕реНрдЯ рдХрд░рдиреЗ рд╕реЗ рдореЗрд░реА рдУрд░ рд╕реЗ рдПрдХ рдирд┐рд░реАрдХреНрд╖рдг рдерд╛ред рдпреВрдЖрд░рдПрд▓ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ {{API_URL}}/api/v1/schools/?offset=49&course_name=Nihongo рд╣реИ рд▓реЗрдХрд┐рди рдореИрдВ рдХрдо рднреНрд░рдорд┐рдд рд╣реЛрдиреЗ рдХреЗ рд▓рд┐рдП offset рдХреЛ рд╣рдЯрд╛ рд░рд╣рд╛ рдерд╛ рдФрд░ рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд рдХрд░рдиреЗ рдореЗрдВ рд╕рдлрд▓ рд░рд╣рд╛

рдЖрдкрдХрд╛ рдЙрджрд╛рд╣рд░рдг рдореЗрд░реЗ рд▓рд┐рдП рдХреЛрдИ рд╕реНрдкрд╖реНрдЯ рдореБрджреНрджрд╛ рдирд╣реАрдВ рдЙрдард╛рддрд╛ рд╣реИред рдореЗрд░рд╛ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рдЕрдиреБрдорд╛рди рдпрд╣ рд╣реИ рдХрд┐ рдПрдкреАрдЖрдИ рджреГрд╢реНрдп рдореЗрдВ рдХреБрдЫ рд╕рдорд╕реНрдпрд╛ рд╣реИред рдХреНрдпрд╛ рдЖрдкрдиреЗ рдЕрдкрдиреА рд╕реЗрдЯрд┐рдВрдЧ/рджреГрд╢реНрдп рдХреЗ filter_backends рдореЗрдВ DjangoFilterBackend рдЬреЛрдбрд╝рд╛ рд╣реИ? рдФрд░ рдХреНрдпрд╛ рдЖрдкрдиреЗ filterset_class = SchoolFilter рд╕реЗрдЯ рдХрд┐рдпрд╛ рд╣реИ (рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рдпрд╣ filter_class рдХрд░рддрд╛ рдерд╛)?

рдЕрд░реЗ @rpkilby

рдореЗрд░реЗ рдкрд╛рд╕ рд╡реНрдпреВ рдХреЛрдб рд╣реИ

class SchoolViewSet(ViewSet):
    http_method_names = ["get", "post"]

    def list(self, request):
        all_school_models = SchoolModel.objects.all()

        filtered_school_models = SchoolFilter(request.GET, queryset=all_school_models)
        # import pdb
        # pdb.set_trace()

        paginator = HeaderLinkPagination()
        current_page_results = paginator.paginate_queryset(filtered_school_models.qs,
                                                           request)

        all_schools_serializer = SchoolSerializer(current_page_results,
                                                  many=True)
        response_data = {
            "schools": all_schools_serializer.data
        }

        response_to_return = paginator.get_paginated_response(response_data)
        return response_to_return

pdb.set_trace() рд╡рд╣ рдЬрдЧрд╣ рд╣реИ рдЬрд╣рд╛рдВ рдореИрдВрдиреЗ рдЙрдкрд░реЛрдХреНрдд рдкреВрдЫрддрд╛рдЫ рдкреЛрд╕реНрдЯ рдХреА рдереАред

рдореИрдВрдиреЗ DjangoFilterBackend рдХреЛ рд╕реЗрдЯ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдХреНрдпреЛрдВрдХрд┐ рдореИрдВрдиреЗ рдорд╛рди рд▓рд┐рдпрд╛ рдерд╛ рдХрд┐ рдпрд╣ рдПрдХ рд╡реИрд╢реНрд╡рд┐рдХ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдерд╛ рдФрд░ рдореИрдВ рдХреЗрд╡рд▓ рдПрдХ рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ рдкрд░ рдЗрд╕рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░ рд░рд╣рд╛ рд╣реВрдВред

рдЗрд╕рдХреЗ рдЕрддрд┐рд░рд┐рдХреНрдд рдореИрдВрдиреЗ рдЗрд╕реЗ рдЙрд╕реА рдХрд╛рд░рдг рд╕реЗ рдХрдХреНрд╖рд╛ рдХреЗ рд▓рд┐рдП рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рд╣реИред

рдпрд╣ рдЬрд╛рдирдирд╛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ рдХрд┐ рдореИрдВ рдЗрд╕ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ SchoolModel рдХреЗ рдирд╛рдо рдкрд░ рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ рд▓реЗрдХрд┐рди рдЬрдм рдореИрдВ рд░рд┐рд╡рд░реНрд╕ рдлреЙрд░реЗрди рдХреА school_course рдкрд░ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░рддрд╛ рд╣реВрдВ рддреЛ рдпрд╣ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред

рдЧреЛрдЪрд╛ - рддреЛ рдЖрдк рд╕реАрдзреЗ рджреГрд╢реНрдп рдореЗрдВ рдлрд╝рд┐рд▓реНрдЯрд░рд╕реЗрдЯ рдмрдирд╛ рд░рд╣реЗ рд╣реИрдВред рдЙрд╕ рд╕реНрдерд┐рддрд┐ рдореЗрдВ, рд╣рд╛рдБ, рдЖрдкрдХреЛ рдлрд╝рд┐рд▓реНрдЯрд░ рдмреИрдХрдПрдВрдб/рдлрд╝рд┐рд▓реНрдЯрд░рд╕реЗрдЯ рд╡рд░реНрдЧ рд╕реЗрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реЛрдЧреАред

рдЖрдк рдпрд╣ рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд╛рдВрдЪ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рдбреЗрдЯрд╛ рд╕рд╣реА рджрд┐рдЦрддрд╛ рд╣реИ, рдпрджрд┐ рдХреЛрдИ рддреНрд░реБрдЯрд┐ рдереА, рдпрд╛ рдпрджрд┐ SQL рдХреНрд╡реЗрд░реА рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдмрдирд╛рдИ рдЧрдИ рд╣реИред рдХреЛрд╢рд┐рд╢:

filtered_school_models = SchoolFilter(request.GET, queryset=all_school_models)
print(filtered_school_models.data)
print(filtered_school_models.errors)
print(filtered_school_models.qs)
print(str(filtered_school_models.qs.query))

@rpkilby рдпрд╣рд╛рдБ рд╡рд╣реА рд╣реИ рдЬреЛ рдореИрдВ рджреЗрдЦ рд░рд╣рд╛ рд╣реВрдБ

<QueryDict: {'offset': ['49'], 'course_name': ['Nihongo']}>

<QuerySet [<SchoolModel: SchoolModel object (51)>]>
SELECT "piano_gym_api_schoolmodel"."id", "piano_gym_api_schoolmodel"."name", "piano_gym_api_schoolmodel"."school_board_id" FROM "piano_gym_api_schoolmodel" INNER JOIN "piano_gym_api_schoolcoursemodel" ON ("piano_gym_api_schoolmodel"."id" = "piano_gym_api_schoolcoursemodel"."school_id") WHERE UPPER("piano_gym_api_schoolcoursemodel"."name"::text) LIKE UPPER(%Nihongo%) ORDER BY "piano_gym_api_schoolmodel"."id" ASC

@rkilby рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдореЗрд░реЗ рдкреЗрдЬрд┐рдиреЗрдЯрд░ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд╕рд╛рде рдПрдХ рдореБрджреНрджрд╛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдЬреЛ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ

class HeaderLinkPagination(LimitOffsetPagination):
    default_limit = settings.DEFAULT_LIMIT
    max_limit = settings.DEFAULT_MAX_LIMIT
    min_limit = settings.DEFAULT_MIN_LIMIT
    min_offset = settings.DEFAULT_MIN_OFFSET
    max_offset = settings.DEFAULT_MAX_OFFSET

    def get_paginated_response(self, data):
        next_url = self.get_next_link()
        previous_url = self.get_previous_link()

        links = []
        header_data = (
            (previous_url, "prev"),
            (next_url, "next"),
        )
        for url, label in header_data:
            if url is not None:
                links.append("<{}>; rel=\"{}\"".format(url, label))

        headers = {"Link": ", ".join(links)} if links else {}

        return Response(data, headers=headers)

    def paginate_queryset(self, queryset, request, view=None):

        limit = request.query_params.get("limit")
        offset = request.query_params.get("offset")

        if limit is None:
            limit = settings.DEFAULT_LIMIT

        if offset is None:
            offset = settings.DEFAULT_OFFSET

        limit = int(limit)
        if limit > self.max_limit:
            error_message = ("Limit should be less than or equal to {0}"
                             ).format(self.max_limit)
            errors = {"limit": [error_message]}
            raise ValidationError(errors)
        elif limit < self.min_limit:
            error_message = ("Limit should be greater than or equal to {0}"
                             ).format(self.min_limit)
            errors = {"limit": [error_message]}
            raise ValidationError(errors)

        offset = int(offset)
        if offset > self.max_offset:
            error_message = ("Offset should be less than or equal to {0}"
                             ).format(self.max_offset)
            errors = {"offset": [error_message]}
            raise ValidationError(errors)
        elif offset < self.min_offset:
            error_message = ("Offset should be greater than or equal to {0}"
                             ).format(self.min_offset)
            errors = {"offset": [error_message]}
            raise ValidationError(errors)
        import pdb
        pdb.set_trace()

        return super(self.__class__, self).paginate_queryset(queryset, request, view)

рдЕрдЧрд░ рдЖрдкрдХреЛ рдХреЛрдИ рд╕рдорд╛рдзрд╛рди рджрд┐рдЦрд╛рдИ рджреЗ рддреЛ рдХреГрдкрдпрд╛ рдореБрдЭреЗ рдмрддрд╛рдПрдВред рдЬрдм рддрдХ рдореИрдВ рдЗрд╕реЗ рдмрд╛рд╣рд░ рдирд┐рдХрд╛рд▓рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рддрд╛ рд╣реВрдВ, рддрдм рддрдХ рдмрдиреЗ рд░рд╣реЗрдВред

рдореЗрд░рд╛ рдЕрдиреБрдорд╛рди рд╣реИ рдХрд┐ рдпрд╣ рджрд┐рдП рдЧрдП рдСрдлрд╕реЗрдЯ рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рд╣реИред рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд┐рдП рдЧрдП рдХреНрд╡реЗрд░реАрд╕реЗрдЯ рдореЗрдВ рдХреЗрд╡рд▓ 1 рдСрдмреНрдЬреЗрдХреНрдЯ рд╣реИ, рд▓реЗрдХрд┐рди рдЕрдиреБрд░реЛрдз рдкреЗрдЬрд┐рдиреЗрдЯрд░ рдХреЛ рдкрд╣рд▓реЗ 49 рдЖрдЗрдЯрдо рдХреЛ рдЫреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣ рд░рд╣рд╛ рд╣реИред рдЕрдЧрд░ рдЖрдкрдХреЛ рдЬрд╛рдВрдЪрдирд╛ рдерд╛

print(filtered_school_models[49:])

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЖрдкрдХреЛ <QuerySet []> рдорд┐рд▓реЗрдЧрд╛ред

рдмрдВрдж рдХрд░рдирд╛, рдЬреИрд╕рд╛ рдХрд┐ рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рдкреЗрдЬрд┐рдиреЗрд╢рди рдореБрджреНрджрд╛ рд╣реИред

рдЕрд░реЗ @rpkilby

рдореИрдВ рдмрд╕ рдЗрддрдирд╛ рдХрд╣рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдЗрд╕ рдорд╛рдзреНрдпрдо рд╕реЗ рдореБрдЭрд╕реЗ рдмрд╛рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд-рдмрд╣реБрдд рдзрдиреНрдпрд╡рд╛рджред рдпрд╣ рдПрдХ django-filter рд╕рдорд╕реНрдпрд╛ рднреА рдирд╣реАрдВ рдереАред рдореИрдВ рд╕рд┐рд░реНрдл рдПрдХ рдбрд┐рдВрдЧрд╕ рдерд╛ред рдпреВ рд░рдмрд░ рдбрдХрд┐рдВрдЧ рдореА рд╕реБрдкрд░ рд╕реБрдкрд░ рд╕рд░рд╛рд╣реЗ рдЧрдП рд╣реИрдВред

рдЖрдк рдЕрджреНрднреБрдд рд╣реИрдВ, рдФрд░ рдПрдХ рдмрд╛рд░ рдлрд┐рд░, рдЗрд╕ рдЕрджреНрднреБрдд рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж!

рдЪрд┐рдВрддрд╛ рди рдХрд░реЗрдВред рдорджрдж рдХрд░рдиреЗ рдореЗрдВ рдЦреБрд╢реА рд╣реЛрдЧреА!

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

jnegro picture jnegro  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

lalzada picture lalzada  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

inakrin picture inakrin  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

blueyed picture blueyed  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

csarcom picture csarcom  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ