Giter Club home page Giter Club logo

django-restql's Introduction

Hi there! 👋

I'm Yezy, I'm making the world a better place to developers by creating great and simple to use developer tools.

I like listening to music and watching movies. If you would like to discuss anything about my work or personal projects, you can check me on Twitter.

django-restql's People

Contributors

amoki avatar ashleyredzko avatar resurrexi avatar yezyilomo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-restql's Issues

Allow excluding fields when querying

When using restql, we found that the filtering as-is is great if there are not a high number of fields. But sometimes we have a case where we'd like everything except a handful of fields on a larger serializer. These fields might be nested and trying the whitelist approach is difficult or possibly too long for the url.

Thoughts on either adding an additional query param for exclusion (such as exclude=...?) or providing some syntax to specify exclusion (something like {course{-books}})?

Definitely willing to help implement, just curious if there's a solid enough case for it. Exclusion doesn't seem too out there to integrate into the field logic on the mixin.

amazing project! what about supporting drf-dynamic-fields syntax too?

Very nice work. In the readme says:

My intention is to extend the capability of drf-dynamic-fields

What about supporting the regular django syntax too? Something like:

fields=id,username,groups__id,groups__name

It seems we could write a pre-processing step to convert fields into query:

from collections import defaultdict


def fields2query(fields_string):
    fields_list = [i for i in (fields_string or "").split(",") if i]

    def _f2q(fields_list):
        query, fields_dict = "", defaultdict(list)

        for i in sorted(set(fields_list)):
            split = i.split("__", 1)

            if len(split) == 2:
                fields_dict[split[0].strip()].append(split[1].strip())
            else:
                fields_dict[split[0].strip()] = []

        for field, nested in sorted(fields_dict.items()):
            new_query = (field + ' ' + _f2q(nested)) if nested else field
            query = ", ".join(i for i in [query, new_query] if i)

        return "{{{0}}}".format(query) if query else query

    return _f2q(fields_list)

I wrote tests also:

def test_fields_to_query():
    # simple case with empty string
    assert fields2query("") == ""

    # weird commas
    assert fields2query(",,") == ""

    # one field only
    assert fields2query("username") == "{username}"

    # two fields
    assert fields2query("username,id") == "{id, username}"

    # funny commas
    assert fields2query("username,,id,,,") == "{id, username}"

    # one level nested
    assert (
        fields2query("username,id,location__country, location__region")
        == "{id, location {country, region}, username}"
    )

    # two level nested
    assert (
        fields2query(
            "username,id,location__country__capital, location__country__population,location__region"
        )
        == "{id, location {country {capital, population}, region}, username}"
    )

    # two, two level nested
    assert (
        fields2query(
            "username,id,location__country__capital, location__country__population,location__region, location__region__timezone"
        )
        == "{id, location {country {capital, population}, region {timezone}}, username}"
    )

Implementation

We could by default support both syntax by checking if __ is in query or if query does not start with {. Let me know if you would be interested in a PR.

I think this will be very attractive for drf-dynamic-fields users as we wouldn't need to refactor any of our code in order to benefit from this very nice package.

Avoid calling select_related and prefetch_related without arguments.

According to Django doc on https://docs.djangoproject.com/en/2.2/ref/models/querysets/#select-related

There may be some situations where you wish to call select_related() with a lot of related objects, or where you don’t know all of the relations. In these cases it is possible to call select_related() with no arguments. This will follow all non-null foreign keys it can find - nullable foreign keys must be specified. This is not recommended in most cases as it is likely to make the underlying query more complex, and return more data, than is actually needed.

So with this in mind we have to avoid calling select_related and prefetch_related if we have no arguments.

Add an option to specify/restrict fields to be returned by nested field

something like

class BookSerializer(DynamicFieldsMixin, ModelSerializer):
    class Meta:
        model = Book
        fields = ['id', 'title', 'author']

class CourseSerializer(DynamicFieldsMixin, ModelSerializer):
    # Return only specified fields with fields kwarg
    books = BookSerializer(many=True, read_only=True, fields=['title']) 
    class Meta:
        model = Course
        fields = ['name', 'code', 'books']

class CourseSerializer(DynamicFieldsMixin, ModelSerializer):
    # Exclude/Restrict specified fields with exclude kwarg
    books = BookSerializer(many=True, read_only=True, exclude=['author'])  
    class Meta:
        model = Course
        fields = ['name', 'code', 'books']

This is important for a use case where you don't want to retrieve all fields of a nested object for some reasons, so you don't want your nested serializer to behave the way it does when it's used directly(Not as a nested field).
For implementation details refer to Dynamically modifying fields

Querying for '__in'

I need to be able to filter an API on a list of IDs. I have amended my filter to allow for this, but I cannot find a syntax that will get through to my filter. Is it possible?

It seems anything with a comma in, breaks the parsing and results in a QuerySyntaxError being returned, e.g:

GET /?query=(id__in:1,2){*}

[
    "QuerySyntaxError: expecting '{' on (id__in:1,2){*}"
]
GET /?query=(id__in:[1,2]){*}

[
    "QuerySyntaxError: expecting '{' on (id__in:[1,2]){*}"
]

avoid requesting unnecessary data in SQL

Something that I really like about drf-dynamic-fields is that they simplify the SQL query, if you are just asking for fields=pk then the SQL avoids making prefetched queries. Wondering if this could be added to django-restql.

Toggle automatic application on EagerLoadingMixin

We have a few cases where we might not want to apply the EagerLoadingMixin get_queryset by default. For example, we might have our own prefetching/joining we want to do when someone doesn't send in a query. Currently, the mixin will return a query result even if a user did not send one in and will then apply the prefetching and joining specified on the view.

I'm imagining this as a boolean field on the mixin, such as auto_apply_eager_loading or some equivalent, and that field would be checked in the overridden get_queryset before attempting to apply it.

Receiving error when attempting to use NestedField

Getting an error in NestedField when attempting to use a nested serializer with allow_null=True. This occurs on update.

Error raised is:

Cannot call `.is_valid()` as no `data=` keyword argument was passed when instantiating the serializer instance.

I have verified that this happens when the data passed in is DRF's empty.

EDIT: Verified that this is actually on update.

How do I toggle between nested and flat values using the same serializer?

Hi, this might not be an issue, but just a lack of understanding on my part. I am trying to cut down on the number of serializers that I use, and I came across this package. I want to be able to sort of toggle between nested and flat values for my foreign key fields, and many-to-many fields, depending on the query params at the end of the api call. However, I tried doing that, but it does not seem to work. Here is my code

models.py

`
class SalesProject(models.Model):
sales_project_name = models.CharField(max_length=100)
userProfile = models.ManyToManyField(
'UserProfile', through='ProjectUser', blank=True, related_name="team")

class UserProfile(models.Model):
contact_number = PhoneNumberField(blank=True)
email = models.EmailField(max_length=255)
email_password = models.CharField(max_length=255)
`

serializers.py

`
class UserProfileDynamicSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = 'all'

class SalesProjectDynamicSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
userProfile = UserProfileDynamicSerializer(many=True, read_only=True)
class Meta:
model = SalesProject
fields = 'all'
`

when calling the api without any query

`
"userProfile": [
{
"id": 1,
"contact_number": "-",
"email": "-",
"email_password": "-",
}
],
"sales_project_name": "Ending Scene 222",

`

is it possible for me to return userProfile: [1], when there are no queries? or even when I specify the query as 'userProfile', it still returns me the nested object for userProfile, and I am not sure how to return it as a flat list. Im sorry if I misunderstood anything, thanks for the help. Look forward to your reply, thanks once again!

NestedField breaks partial_update (PATCH request)

I have a nested serializer in which I need to accept a pk for the nested model. So my serialzer looks something like this:

class AccountSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
    client = NestedField(ClientSerializer, accept_pk=True)
    class Meta:
        model = Account
        fields = ('client', 'code')

So when I send a PATCH request to update the Account's code, I send this:

PUT /api/account/1/
{'code': 'Foo'}

Now despite this being a PATCH (not a PUT), and therefore required fields should be ignored (as per standard DRF behaviour), I get a 400 with the error:
{"client": ["This field is required."]}

I tried making the field required=False, to see what would happen (despite this being incorrect, since it IS required for create/POST and update/PUT), I get a 400 with the error:
{"client": ["This field may not be null."]}

Note: I am not send null, I'm just not sending the field at all.

For interest, I tried also making the field allow_null=True, and indeed, it goes all the way to the database with a null value for client, and explodes.

I think I've narrowed down the issue to:

elif data == empty and default == empty:
data = ""

I'm not sure why, if the field is empty and not required, that it sets it to "", and then in the next line to None. If the empty value is passed along, it validates correctly. So I'll make a PR for this, and hopefully this won't create any other quirks.

Avoid parsing a query twice if the request involves both DynamicFieldsMixin and EagerLoadingMixin

Since DynamicFieldsMixin and EagerLoadingMixin use a query request parameter, they both tend to parse a raw query individually, so parsing is done twice but the result is the same, we could avoid this by parsing only once and save the result to the request object so that when a parsed query is needed we won't need to parse a raw query again instead we use the one saved on the request. This could improve performance.

Question about NestedModelSerializer

Hello,

I recently encounter an issue with using NestedModelSerializer for updating a nested field. I am wondering if it is a bug.

For example, I have the nested serializer set up this way:

class LocationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Location
        fields = ["id", "city", "country"]

class PropertySerializer(NestedModelSerializer):
    location = NestedField(LocationSerializer, required=False, allow_null=True)
    ..... other fields

I could create a property object through PropertySerializer without pass in a location data due to the fact that is not required.

Later on, I decided to patch a location into this property with request payload like

"location": {
        "city": "Newyork",
        "country": "USA"
  }

I noticed the location object has been created but it is not attached to the property.

I did a little research and found out it may be related to some logic in django_restql.mixins.NestedUpdateMixin.update_writable_foreignkey_related.

            nested_obj = getattr(instance, field)
            serializer = serializer_class(
                nested_obj,
                **kwargs,
                data=values,
                context=self.context,
                partial=serializer.is_partial
            )
            serializer.is_valid()
            if values is None:
                setattr(instance, field, None)
                objs.update({field: None})
            else:
                obj = serializer.save()
                objs.update({field: nested_obj})

In this code above, if the nested_obj did not exist, it will not attach the newly created obj back to instance. I wonder if it has been done intentionally? If so, I wonder what is the reason, and is there a proper way to patch a nested object into a parent object that did not originally exist?

Thank you!

Minor bug in NestedField if nested serializer allows empty data

I ran into an issue using NestedFields when it wraps a serializer that allows no data body when POSTing.

What I want to be able to achieve is embed the a serializer as a nested serializer in another serializer. With this nested serializer, I want to be able to either:

  1. Automatically create the instance for the nested serializer if no data is available for the serializer.
  2. Create the instance if data is available for the nested serializer.

The problem I face is that in order to achieve bullet point 1, I can't wrap the nested serializer field in NestedField, and I would have to then override create and update in the parent serializer to achieve bullet point 2.

As an example, consider the following model definitions and their serializer:

# models.py
from django.db import models
from django.utils.crypto import get_random_string
from django.utils.timezone import now


class LotNumber(models.Model):
    lot_number = models.CharField(max_length=6, primary_key=True, blank=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    @staticmethod
    def _generate_lot_number(length=6):
        return get_random_string(length=length)

    def save(self, *args, **kwargs):
        if not self.lot_number:
            self.lot_number = self._generate_lot_number(length=6)
        super().save(*args, **kwargs)


class Inventory(models.Model):
    sku_code = models.CharField(max_length=32)
    lot_number = models.ForeignKey(LotNumber, blank=True, null=True, on_delete=models.PROTECT)
    finish_date = models.DateField(default=now)

    def save(self, *args, **kwargs):
        if not self.lot_number:
            lot_class = self._meta.get_field("lot_number").related_model
            new_lot = lot_class.objects.create()
            self.lot_number = new_lot
        super().save(*args, **kwargs)


# serializers.py
from rest_framework import serializers

from django_restql.serializers import NestedModelSerializer
from django_restql.fields import NestedField

from .models import LotNumber, Inventory


class LotNumberSerializer(serializers.ModelSerializer):
    class Meta:
        model = LotNumber
        fields = "__all__"


class InventorySerializer(serializers.ModelSerializer):
    lot_number = LotNumberSerializer(required=False)

    class Meta:
        model = Inventory
        fields = "__all__"


class NestedInventorySerializer(NestedModelSerializer):
    lot_number = NestedField(LotNumberSerializer, required=False)

    class Meta:
        model = Inventory
        fields = "__all__"

If I were to make a POST request at the InventorySerializer endpoint, I would be able to create an Inventory instance without passing data for the lot_number field because the save method in the LotNumber model automatically creates the data. The LotNumberSerializer would also consider the empty data as valid. The response would be something like:

{
    "id": 1,
    "sku_code": "ABC-PRODUCT",
    "lot_number": {
	    "lot_number": "P519CK",
        "is_active": true,
        "created_at": "2020-03-18T12:12:39.943000Z",
        "updated_at": "2020-03-18T12:12:39.943000Z"
    }
}

This only achieves bullet point 1 from earlier. If I wanted to inject my own lot_number value, I have to override the create and update methods in InventorySerializer, which complicates things.

Now, if I were to make a POST request at the NestedInventorySerializer endpoint, I would be able to create the Inventory instance if I did pass data for the lot_number field. However, if I attempt bullet point 1 on this serializer, I get the following response:

{
    "lot_number": {
        "non_field_errors": [
            "No data provided"
        ]
    }
}

I found a temporary fix whereby requiring the lot_number field in the parent serializer and setting a default={} on the field resolved the problem. However I don't think this aligns with the intent of the NestedField wrapper, where arguments should behave the same way as when the wrapper is not used.

Instead, I think the fix for this is in fields.py in the to_internal_value method of the BaseNestedFieldSerializer class. Specifically in line 278, the code should be data = {}. Technically, the code could be moved to line 275 and lines 276-278 can be deleted. This fix should not affect scenarios where a nested serializer does require data and data isn't passed, due to the fact that calling is_valid in this scenario will return False.

Bug with parsing CSV-type filters

I'm having issue with restql parsing filter queries when some filters allow comma separated values, specifically ordering filters where multiple fields can be ordered by, e.g. /api/employees/?query=(ordering:last_name,first_name){*}

The error response I get is:

[
    "QuerySyntaxError: expecting '{' on (ordering:last_name,"
]

I don't get the error if I only order by one field. Also, if I attempt to chain the ordering, e.g. /api/employees/?query=(ordering:last_name,ordering:first_name){*}, I get the result that it only orders by the last ordering element (by first_name). It doesn't do the hierarchical ordering that I desire.

The filter backend I'm using is django-filter (link to its OrderingFilter doc), though I doubt it's relevant because django's built-in ordering filter also supports CSV values.

I know the bug happens here but I'm no pypeg2 expert. I'm guessing one potential fix is to maybe change the delimiter to |, e.g. query=(ordering:field1,field2|filter2:field3|filter3:fieldX|...){*}?

Is there an option to turn off the dynamic fields option (DynamicFieldsMixin) even when passing the query?

We recently run into a situation that the children serializer break inside the to_representation method.
For example

class ParentSerializer(DynamicFieldsMixin, serializer.ModelSerializer):
    ...
    def to_representation(self, instance):
        representation = super().to_representation(instance)
        # Manually set the representation here. Imagine that we need to do some extra work that can't be put
        # into a serializer method field for some reason.
        # Because we pass `self.context`, any restql querying is using the parent query fields, so those fields
        # might not exist on the child, which raises a parser error.
        representation["children"] = ChildSerializer(data=instance.child, context=self.context)
class ChildSerializer(DynamicFieldsMixin, serializers.ModelSerializer)

As a result, ChildSerializer did not work because it did not pass correct data in the query params which were for the parent serializer. I wonder if we have an option to toggle dynamic field functionality when instantiating the serializer?

Thanks,
Tao

Support shared eager loading fields

Sometimes, more than one field might want to share the same prefetch related. When doing this with strings (such as prefetch_related("related_thing")), multiple instances do not cause issues. However, when attempting to prefetch using a Prefetch object, it can cause issues if the same query is specified with the same to_attr.

Example:

prefetch_related = {
    "books": "books",
    "latest_course_book": Prefetch("books", queryset=Book.objects.all().order_by("-id"), to_attr="reversed_books"),
    "course_book_list": Prefetch("books", queryset=Book.objects.all().order_by("-id"), to_attr="reversed_books"),
}

Both of these fields care about either an order or the last book. Ordering by the reverse ID is ideal when prefetching then, but we don't want to do it more than once (which is what happens if we specify different to_attr) and providing the same to_attr causes an error. Instead, I propose a way to specify shared prefetch_related values to prevent this issue:

prefetch_related = {
    "books": "books",
}
shared_prefetch_related = [
    {
        "fields": ["latest_course_book", "course_book_list"],
        "prefetch": Prefetch("books", queryset=Book.objects.all().order_by("-id"), to_attr="reversed_books"),
    }
]

create_ops & update_ops kwargs doesn't work

Reproducing

class CourseSerializer(DynamicFieldsMixin, NestedModelSerializer):
    books = NestedField(BookSerializer, many=True, required=False, create_ops=[], update_ops=[])

    class Meta:
        model = Course
        fields = ['name', 'code', 'books']

This shouldn't allow any operation, but all operations works as if there is no restriction.

Empty NestedField produces error on create/update on DRF 3.9.x

When attempting to create/update using a serializer that has a NestedField serializer that has allow_null set, if a blank/empty value is passed in (or no value is passed in), this stack trace pops up (some info redacted for readability and privacy).

I managed to narrow down the issue to this portion of the NestedField, that forces the call to to_internal_value. However the data value is the empty value from DRF, which errors when attempting to validate the serializer, since data is empty.

I'm unsure what the issue was the necessitated this, but is there some other way to fix it, or some way to check that this step needs to be done based on the drf version (for example, by importing rest_framework and checking rest_framework.VERSION)?

:art: Double array feels weird

GET /users/?query=[["id", "username"]] feels weird.

  • GET /users/?query=[["id", "username"]] and GET /users/15/?query=["id", "username"] must have different queries while behaving the same way.
  • It would imply I can do stuff like [["id", "username"], ["id"]] if I want different serializations for my first and second objects.
  • GET /users/?query=[["id", "username", {"groups": [[ "id", "name", {"events": [[ "date", "name" ]]}]]}]] is hard to follow. GET /users/?query=["id", "username", {"groups": [ "id", "name", {"events": ["date", "name"] }]}] would semantically do the same.

Is this a limitation of distfier?

Nested File Field

Is there a recommended method to work with a FileField in a nested serializer ?

In this example:

class Customer:
    signature = FileField()
    name = CharField()

class Ticket:
    customer = Customer()
    amount = FloatField()
    date = DateField()
    ...

How would a Ticket POST request containing a customer signature be handled?

Getting last related object

Not sure if this is an issue or I'm just doing something wrong.
My models:

class Application(models.Model):
    customer = models.ForeignKey("Customer",
                                 related_name="applications",
                                 on_delete=models.CASCADE)
    status = models.CharField(max_length=50,
                              choices=ApplicationStatus.choices,
                              default=ApplicationStatus.INCOMPLETE)


class Partner(models.Model):
    name = models.CharField(max_length=31)


class Offer(models.Model):
    application = models.ForeignKey("core.Application",
                                    related_name="offers",
                                    on_delete=models.CASCADE)
    partner = models.ForeignKey("core.Partner",
                                related_name="partners",
                                on_delete=models.CASCADE)
    status = models.CharField(max_length=50,
                              choices=OfferStatus.choices,
                              default=OfferStatus.NEW)


class Customer(models.Model):
    """ Customer model """

    first_name = models.CharField(max_length=31)
    last_name = models.CharField(max_length=31)

serializers:

class PartnerSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
    class Meta:
        model = Partner
        fields = ["id", "name"]


class OfferSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
    partner = PartnerSerializer(read_only=True)

    class Meta:
        model = Offer
        fields = ["id", "partner", "status"]


class ApplicationSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
    """ Application serializer """

    offers = OfferSerializer(many=True, read_only=True)

    class Meta:
        model = Application
        fields = [
            "id", "customer", "offers", "status"
        ]


class CustomerSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
    """ Customer serializer """

    applications = ApplicationSerializer(many=True, read_only=True)

    class Meta:
        model = Customer
        fields = [
            "id",
            "first_name",
            "last_name",
        ]

and views:

class ApplicationViewSet(ModelViewSet):
    serializer_class = ApplicationSerializer
    queryset = Application.objects.all()
    filter_fields = {"status": ["icontains"]}


class CustomerViewSet(ModelViewSet):
    serializer_class = CustomerSerializer
    queryset = Customer.objects.all()
    filter_fields = {
        "first_name": ["icontains"],
        "last_name": ["icontains"],
        "applications__status": ["exact"],
    }


class PartnerViewSet(ModelViewSet):
    serializer_class = PartnerSerializer
    queryset = Partner.objects.all()


class OfferViewSet(ModelViewSet):
    serializer_class = OfferSerializer
    queryset = Offer.objects.all()

And the issue is that query /api/v2/customers/?query=(first_name:Mirdza){id,first_name,last_name,applications(status:INCOMPLETE){id,status}}
returns data like this:

		{
            "id": 1,
            "first_name": "Mirdza",
            "last_name": "Kalniņa",
            "applications": [
                {
                    "id": 1,
                    "status": "POSITIVE_OFFERS"
                }
            ]
        },
        {
            "id": 2,
            "first_name": "Jūla",
            "last_name": "Krūmiņa",
            "applications": [
                {
                    "id": 2,
                    "status": "NO_ACTION"
                },
                {
                    "id": 3,
                    "status": "WAITING_MORE_INFO"
                },
                {
                    "id": 4,
                    "status": "WAITING_MORE_INFO"
                }
            ]
        },
		{
            "id": 3,
            "first_name": "Mirdza",
            "last_name": "Roze",
            "applications": [
                {
                    "id": 9,
                    "status": "INCOMPLETE"
                }
            ]
        },
...

What I expected was:

		{
            "id": 3,
            "first_name": "Mirdza",
            "last_name": "Roze",
            "applications": [
                {
                    "id": 9,
                    "status": "INCOMPLETE"
                }
            ]
        },

First of all I want always to get the last application for a customer and in case of the given query, only customers who have last application in a given status.
Is it even possible to achieve that?

Unable to filter by datetime

When I try to specify a datetime as a filter, it breaks the restql parsing.
Example query:

?query=(last_queried_on__lt:2020-04-27T23:02:32Z){{*}}

When I attempt that query, I get a 400:

QuerySyntaxError: expecting \'{\' on (last_queried_on__lt

Support prefetching and selecting related fields dynamically according to a query parameter

Hey there!

Have started recently using the project and super impressed! Very helpful to be able to narrow down the fields returned on the fly.

One consideration my team and I had was about trying to properly prefetch_related/select_related when we are or are not including fields. For example, if a Course has books, but we only ask for id, then we should not prefetch books. But if we do include books, we definitely want to prefetch them to prevent the list queries from getting out of control.

We have made our own internal implementation and mixin, but wanted to gauge interest and make sure we take any cases we haven't run into yet into consideration. Thoughts?

when NestedModelSerializer is used, alias field in serializer will throw error.

Not sure if it's just me setting it up wrong or it's actually an issue.

For simplicity, here's an example:

class PersonSerializer(NestedModelSerializer):
    nickname = serializers.CharField(source="name")
    
    class Meta:
        fields = ("nickname")

And when I do POST request with:

{
    "nickname": "test"
}

It complains about a KeyError (traced back to NestedCreateMixin)

If I just use serializers.ModelSerializer, this is not an issue.

NestedCreateMixin does not raise exceptions if serializer is invalid

When attempting to create via a NestedField on a NestedModelSerializer, both create_writable_foreignkey_related and bulk_create_objs check is_valid() but do not raise exceptions if the serializer is invalid. This leads to an error if an invalid value is somehow passed in.

This also applies to NestedUpdateMixin's update_writable_foreignkey_related and bulk_create_many_to_many_related.

exclude kwarg not working on 0.10.1 but is ok on 0.8.4

class IssueSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
    task_detail = TaskDetailSerializer(
        read_only=True, exclude=['cashflow', 'cashflow_pending'])

    class Meta:
        model = Issue
        fields = '__all__'

I am upgrade from version 0.8.4, it works fine when version 0.8.4. But the same code is not working on version 0.10.1.

Typo in CONTRIBUTING.md

There is a typo in the how to contribute section - 'experianced'. it should be 'experienced'.

Add a mixin to convert query arguments into query params

  • This will allow the library to work with any filter backend and any pagination class without requiring integration.
  • With this DEFAULT_BASE_FILTER_BACKEND setting will not be needed anymore as all filter backends will be working out of the box.

Will be able to do

  • GET /users/?query=(page: 2){id, name}
  • GET /users/?query=(limit: 5, offset: 10){id, name}
  • GET /users/?query=(id: 3, name: Bob){id, name}

Hyphens in query filters return an error

I receive the following error when I filter a field using a phrase that contains hyphens in it:
[ "QuerySyntaxError: expecting '{' on (key:splash-screen){" ]

The query I used is the following:
https://domain.com/api/data/?query=(key:splash-screen){*}

When I replace the hyphen with an underscore, the query runs fine. This query does not return any error:
https://domain.com/api/data/?query=(key:splash_screen){*}

The filter used for the "key" field is icontains (case insensitive contains).

`fields` kwarg conflicts with a FK NestedField with `accept_pk` kwarg when updating

I'm running into an issue when PATCHing or PUTing a resource with a nested serializer. For brevity, here's an example:

class EmployeeJobDetailSerializer(DynamicFieldsMixin, NestedModelSerializer):
	employee = NestedField(
		EmployeeSerializer,
		accept_pk=True,
		fields=['employee_id', 'first_name', 'last_name']
	)

    class Meta:
        model = EmployeeJobDetail
		fields = ...

If I'm not mistaken, accept_pk=True essentially converts the nested serializer to a PrimaryKeyRelatedField when creating/updating. However, PrimaryKeyRelatedField does not recognize the fields argument so it throws an error: TypeError: __init__() got an unexpected keyword argument 'fields'.

Is there a way to somehow ignore fields when updating with accept_pk=True? GET works perfectly and only selects the fields that I have specified for the kwarg.

does not work with SerializerMethodField

In case when i use one to many fields i need to pass serializer's id into different serializer.

def get_a_list(self, obj):
    child = b.objects.all()
    serializer = BListSerializer(instance=child, context={"obj_id": obj.id}, many=True)
    return serializer.data

Define query_parameter in settings file.

Currently to use a custom name for query parameter instead of default query, one has to modify fields every serializer that inherits from DynamicFieldsMixin.

Can this be define globally in settings file and DynamicFieldsMixin reads from it if available. Otherwise it uses default.

PATCHing does not work with NestedFields, when the NestedField is omitted

I've installed this latest version that was to resolve this issue, and am trying to patch again, but still getting (new) errors.

Following the example in the original issue with AccountSerializer nesting a ClientSerializer, I am trying to patch to update other attributes of the Account (completely omitting client in my request body), and getting the following error:
ValueError: Cannot assign "<class 'rest_framework.fields.empty'>": "Account.client" must be a "Client" instance.

It seems that while the request does not have client in the JSON, the serializer's validated_data does, with a value of the rest_framework.fields.empty class.

I presume your updated tests covered this case, could I be doing something wrong?

Originally posted by @TheUKDave in #143 (comment)

Extra context from EagerLoadingMixin

Although we can access the view & request objects in the serializer via standard DRF practices, it would be great if the mixin would also add, for example: "eager_loading": self.should_auto_apply_eager_loading to the serializer context.

Some projects have view or other dynamic settings for this and having the mixin make an easier way to check would be helpful when trying to check the value from the serializer (less code but still correct).

Additionally sometimes projects also call into serializers without coming from a view, and thus a convention of using a specific key for the setting, would make it easier for all use cases. They can rely on it (they'll just set their context manually).

Add a way to pass arguments through a query param just like in GraphQL.

Arguments can be used to do filtering, sorting and other stuffs, they are like request params but associated with fields eg

query = (age: 18, sort: name){
    name,
    age,
    location(country: Canada){
        country,
        city
    }
}

In this query we have three arguments age, sort and country

Initially we can allow arguments on nested fields only because passing arguments on flat fields is very rarely even on GraphQL.

Link bug in docs

I just wanted to say that I discovered your package recently and I'm absolutely blown away with what it can do. I was actually thinking of switching from drf to graphene until I found your repo. I'm so impressed I actually want to donate to your project.

Anyways, aside from my impressions, I was looking through your docs and found one minor bug in a section under the Settings tab: https://django-restql.yezyilomo.com/settings/#default_base_filter_backend

In that section, there is a dead link, Filtering with query arguments. I believe that link should point to http://django-restql.yezyilomo.com/querying_data/#filtering-with-query-arguments

I can fix the bug and submit a PR if you want.

Allow flexibility on data returned by nested field

Add an option for user to decide whether to return nested object pk or serialized data, something like allow_pk kwarg but for returned data

CertainSerializer(return_pk=True)

also pk kwarg can be changed to accept_pk to be more clear

CertainSerializer(return_pk=True)

This can be accomplished by overriding to_representation method of a DynamicFieldsMixin

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.