Support use first and last at same time as offset

This commit is contained in:
NateScarlet 2018-12-18 18:11:57 +08:00
parent f76f38ef30
commit 58d846e5e7
No known key found for this signature in database
GPG Key ID: FB24AAE55529F29F
3 changed files with 92 additions and 52 deletions

View File

@ -1,11 +1,10 @@
from functools import partial from functools import partial
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from graphene.relay import ConnectionField, PageInfo
from graphene.types import Field, List
from promise import Promise from promise import Promise
from graphene.types import Field, List
from graphene.relay import ConnectionField, PageInfo
from graphql_relay.connection.arrayconnection import connection_from_list_slice from graphql_relay.connection.arrayconnection import connection_from_list_slice
from .settings import graphene_settings from .settings import graphene_settings
@ -103,39 +102,44 @@ class DjangoConnectionField(ConnectionField):
@classmethod @classmethod
def connection_resolver( def connection_resolver(
cls, cls,
resolver, resolver,
connection, connection,
default_manager, default_manager,
max_limit, max_limit,
enforce_first_or_last, enforce_first_or_last,
root, root,
info, info,
**args **kwargs):
): # pylint: disable=R0913,W0221
first = args.get("first")
last = args.get("last")
if enforce_first_or_last: first = kwargs.get("first")
assert first or last, ( last = kwargs.get("last")
"You must provide a `first` or `last` value to properly paginate the `{}` connection." if not (first is None or first > 0):
).format(info.field_name) raise ValueError(
"`first` argument must be positive, got `{first}`".format(**locals()))
if not (last is None or last > 0):
raise ValueError(
"`last` argument must be positive, got `{last}`".format(**locals()))
if enforce_first_or_last and not (first or last):
raise ValueError(
"You must provide a `first` or `last` value "
"to properly paginate the `{info.field_name}` connection.".format(**locals()))
if max_limit: if not max_limit:
if first: pass
assert first <= max_limit, ( elif first is None and last is None:
"Requesting {} records on the `{}` connection exceeds the `first` limit of {} records." kwargs['first'] = max_limit
).format(first, info.field_name, max_limit) else:
args["first"] = min(first, max_limit) count = min(i for i in (first, last) if i)
if count > max_limit:
raise ValueError(("Requesting {count} records "
"on the `{info.field_name}` connection "
"exceeds the limit of {max_limit} records.").format(**locals()))
if last: iterable = resolver(root, info, **kwargs)
assert last <= max_limit, ( on_resolve = partial(cls.resolve_connection,
"Requesting {} records on the `{}` connection exceeds the `last` limit of {} records." connection, default_manager, kwargs)
).format(last, info.field_name, max_limit)
args["last"] = min(last, max_limit)
iterable = resolver(root, info, **args)
on_resolve = partial(cls.resolve_connection, connection, default_manager, args)
if Promise.is_thenable(iterable): if Promise.is_thenable(iterable):
return Promise.resolve(iterable).then(on_resolve) return Promise.resolve(iterable).then(on_resolve)

View File

@ -1,17 +1,18 @@
from datetime import datetime from datetime import datetime
import pytest import pytest
from graphene import Field, ObjectType, Schema, Argument, Float, Boolean, String
from graphene.relay import Node
from graphene_django import DjangoObjectType
from graphene_django.forms import GlobalIDFormField, GlobalIDMultipleChoiceField
from graphene_django.tests.models import Article, Pet, Reporter
from graphene_django.utils import DJANGO_FILTER_INSTALLED
# for annotation test # for annotation test
from django.db.models import TextField, Value from django.db.models import TextField, Value
from django.db.models.functions import Concat from django.db.models.functions import Concat
from graphene import (Argument, Boolean, Field, Float, ObjectType, Schema,
String)
from graphene.relay import Node
from graphene_django import DjangoObjectType
from graphene_django.forms import (GlobalIDFormField,
GlobalIDMultipleChoiceField)
from graphene_django.tests.models import Article, Pet, Reporter
from graphene_django.utils import DJANGO_FILTER_INSTALLED
pytestmark = [] pytestmark = []
@ -697,3 +698,40 @@ def test_annotation_is_perserved():
assert not result.errors assert not result.errors
assert result.data == expected assert result.data == expected
def test_filter_with_union():
class ReporterType(DjangoObjectType):
class Meta:
model = Reporter
interfaces = (Node,)
filter_fields = ("first_name",)
class Query(ObjectType):
all_reporters = DjangoFilterConnectionField(ReporterType)
@classmethod
def resolve_all_reporters(cls, root, info, **kwargs):
ret = Reporter.objects.none() | Reporter.objects.filter(first_name="John")
Reporter.objects.create(first_name="John", last_name="Doe")
schema = Schema(query=Query)
query = """
query NodeFilteringQuery {
allReporters(firstName: "abc") {
edges {
node {
firstName
}
}
}
}
"""
expected = {"allReporters": {"edges": []}}
result = schema.execute(query)
assert not result.errors
assert result.data == expected

View File

@ -1,21 +1,19 @@
import datetime import datetime
import graphene
import pytest import pytest
from django.db import models from django.db import models
from django.db.models import Q
from django.utils.functional import SimpleLazyObject from django.utils.functional import SimpleLazyObject
from graphene.relay import Node
from py.test import raises from py.test import raises
from django.db.models import Q from ..compat import JSONField, MissingType
import graphene
from graphene.relay import Node
from ..utils import DJANGO_FILTER_INSTALLED
from ..compat import MissingType, JSONField
from ..fields import DjangoConnectionField from ..fields import DjangoConnectionField
from ..types import DjangoObjectType
from ..settings import graphene_settings from ..settings import graphene_settings
from .models import Article, CNNReporter, Reporter, Film, FilmDetails from ..types import DjangoObjectType
from ..utils import DJANGO_FILTER_INSTALLED
from .models import Article, CNNReporter, Film, FilmDetails, Reporter
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -603,7 +601,7 @@ def test_should_error_if_first_is_greater_than_max():
assert len(result.errors) == 1 assert len(result.errors) == 1
assert str(result.errors[0]) == ( assert str(result.errors[0]) == (
"Requesting 101 records on the `allReporters` connection " "Requesting 101 records on the `allReporters` connection "
"exceeds the `first` limit of 100 records." "exceeds the limit of 100 records."
) )
assert result.data == expected assert result.data == expected
@ -644,7 +642,7 @@ def test_should_error_if_last_is_greater_than_max():
assert len(result.errors) == 1 assert len(result.errors) == 1
assert str(result.errors[0]) == ( assert str(result.errors[0]) == (
"Requesting 101 records on the `allReporters` connection " "Requesting 101 records on the `allReporters` connection "
"exceeds the `last` limit of 100 records." "exceeds the limit of 100 records."
) )
assert result.data == expected assert result.data == expected