@@ -64,11 +64,11 @@ from the :code:`ds` instance
64
64
65
65
ds.Query.hero.select(ds.Character.name)
66
66
67
- The select method return the same instance, so it is possible to chain the calls::
67
+ The select method returns the same instance, so it is possible to chain the calls::
68
68
69
69
ds.Query.hero.select(ds.Character.name).select(ds.Character.id)
70
70
71
- Or do it sequencially ::
71
+ Or do it sequentially ::
72
72
73
73
hero_query = ds.Query.hero
74
74
@@ -279,7 +279,7 @@ will generate the request::
279
279
Multiple operations in a document
280
280
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
281
281
282
- It is possible to create an Document with multiple operations::
282
+ It is possible to create a Document with multiple operations::
283
283
284
284
query = dsl_gql(
285
285
operation_name_1=DSLQuery( ... ),
@@ -384,6 +384,305 @@ you can use the :class:`DSLMetaField <gql.dsl.DSLMetaField>` class::
384
384
DSLMetaField("__typename")
385
385
)
386
386
387
+ Directives
388
+ ^^^^^^^^^^
389
+
390
+ `Directives `_ provide a way to describe alternate runtime execution and type validation
391
+ behavior in a GraphQL document. The DSL module supports both built-in GraphQL directives
392
+ (:code: `@skip `, :code: `@include `) and custom schema-defined directives.
393
+
394
+ To add directives to DSL elements, use the :meth: `DSLSchema.__call__ <gql.dsl.DSLSchema.__call__> `
395
+ factory method and the :meth: `directives <gql.dsl.DSLDirectable.directives> ` method::
396
+
397
+ # Using built-in @skip directive with DSLSchema.__call__ factory
398
+ ds.Query.hero.select(
399
+ ds.Character.name.directives(ds("@skip").args(**{"if": True}))
400
+ )
401
+
402
+ Directive Arguments
403
+ """""""""""""""""""
404
+
405
+ Directive arguments can be passed using the :meth: `args <gql.dsl.DSLDirective.args> ` method.
406
+ For arguments that don't conflict with Python reserved words, you can pass them directly::
407
+
408
+ # Using the args method for non-reserved names
409
+ ds("@custom").args(value="foo", reason="testing")
410
+
411
+ It can also be done by calling the directive directly::
412
+
413
+ ds("@custom")(value="foo", reason="testing")
414
+
415
+ However, when the GraphQL directive argument name conflicts with a Python reserved word
416
+ (like :code: `if `), you need to unpack a dictionary to escape it::
417
+
418
+ # Dictionary unpacking for Python reserved words
419
+ ds("@skip").args(**{"if": True})
420
+ ds("@include")(**{"if": False})
421
+
422
+ This ensures that the exact GraphQL argument name is passed to the directive and that
423
+ no post-processing of arguments is required.
424
+
425
+ The :meth: `DSLSchema.__call__ <gql.dsl.DSLSchema.__call__> ` factory method automatically handles
426
+ schema lookup and validation for both built-in directives (:code: `@skip `, :code: `@include `)
427
+ and custom schema-defined directives using the same syntax.
428
+
429
+ Directive Locations
430
+ """""""""""""""""""
431
+
432
+ The DSL module supports all executable directive locations from the GraphQL specification:
433
+
434
+ .. list-table ::
435
+ :header-rows: 1
436
+ :widths: 25 35 40
437
+
438
+ * - GraphQL Spec Location
439
+ - DSL Class/Method
440
+ - Description
441
+ * - QUERY
442
+ - :code: `DSLQuery.directives() `
443
+ - Directives on query operations
444
+ * - MUTATION
445
+ - :code: `DSLMutation.directives() `
446
+ - Directives on mutation operations
447
+ * - SUBSCRIPTION
448
+ - :code: `DSLSubscription.directives() `
449
+ - Directives on subscription operations
450
+ * - FIELD
451
+ - :code: `DSLField.directives() `
452
+ - Directives on fields (including meta-fields)
453
+ * - FRAGMENT_DEFINITION
454
+ - :code: `DSLFragment.directives() `
455
+ - Directives on fragment definitions
456
+ * - FRAGMENT_SPREAD
457
+ - :code: `DSLFragmentSpread.directives() `
458
+ - Directives on fragment spreads (via .spread())
459
+ * - INLINE_FRAGMENT
460
+ - :code: `DSLInlineFragment.directives() `
461
+ - Directives on inline fragments
462
+ * - VARIABLE_DEFINITION
463
+ - :code: `DSLVariable.directives() `
464
+ - Directives on variable definitions
465
+
466
+ Examples by Location
467
+ """"""""""""""""""""
468
+
469
+ **Operation directives **::
470
+
471
+ # Query operation
472
+ query = DSLQuery(ds.Query.hero.select(ds.Character.name)).directives(
473
+ ds("@customQueryDirective")
474
+ )
475
+
476
+ # Mutation operation
477
+ mutation = DSLMutation(
478
+ ds.Mutation.createReview.args(episode=6, review={"stars": 5}).select(
479
+ ds.Review.stars
480
+ )
481
+ ).directives(ds("@customMutationDirective"))
482
+
483
+ **Field directives **::
484
+
485
+ # Single directive on field
486
+ ds.Query.hero.select(
487
+ ds.Character.name.directives(ds("@customFieldDirective"))
488
+ )
489
+
490
+ # Multiple directives on a field
491
+ ds.Query.hero.select(
492
+ ds.Character.appearsIn.directives(
493
+ ds("@repeat").args(value="first"),
494
+ ds("@repeat").args(value="second"),
495
+ ds("@repeat").args(value="third"),
496
+ )
497
+ )
498
+
499
+ **Fragment directives **:
500
+
501
+ You can add directives to fragment definitions and to fragment spread instances.
502
+ To do this, first define your fragment in the usual way::
503
+
504
+ name_and_appearances = (
505
+ DSLFragment("NameAndAppearances")
506
+ .on(ds.Character)
507
+ .select(ds.Character.name, ds.Character.appearsIn)
508
+ )
509
+
510
+ Then, use :meth: `spread() <gql.dsl.DSLFragment.spread> ` when you need to add
511
+ directives to the fragment spread::
512
+
513
+ query_with_fragment = DSLQuery(
514
+ ds.Query.hero.select(
515
+ name_and_appearances.spread().directives(
516
+ ds("@customFragmentSpreadDirective")
517
+ )
518
+ )
519
+ )
520
+
521
+ The :meth: `spread() <gql.dsl.DSLFragment.spread> ` method creates a
522
+ :class: `DSLFragmentSpread <gql.dsl.DSLFragmentSpread> ` instance that allows you to add
523
+ directives specific to the fragment spread location, separate from directives on the
524
+ fragment definition itself.
525
+
526
+ Example with fragment definition and spread-specific directives::
527
+
528
+ # Fragment definition with directive
529
+ name_and_appearances = (
530
+ DSLFragment("CharacterInfo")
531
+ .on(ds.Character)
532
+ .select(ds.Character.name, ds.Character.appearsIn)
533
+ .directives(ds("@customFragmentDefinitionDirective"))
534
+ )
535
+
536
+ # Using fragment with spread-specific directives
537
+ query_without_spread_directive = DSLQuery(
538
+ # Direct usage (no spread directives)
539
+ ds.Query.hero.select(name_and_appearances)
540
+ )
541
+ query_with_spread_directive = DSLQuery(
542
+ # Enhanced usage with spread directives
543
+ name_and_appearances.spread().directives(
544
+ ds("@customFragmentSpreadDirective")
545
+ )
546
+ )
547
+
548
+ # Don't forget to include the fragment definition in dsl_gql
549
+ query = dsl_gql(
550
+ name_and_appearances,
551
+ BaseQuery=query_without_spread_directive,
552
+ QueryWithDirective=query_with_spread_directive,
553
+ )
554
+
555
+ This generates GraphQL equivalent to::
556
+
557
+ fragment CharacterInfo on Character @customFragmentDefinitionDirective {
558
+ name
559
+ appearsIn
560
+ }
561
+
562
+ {
563
+ BaseQuery hero {
564
+ ...CharacterInfo
565
+ }
566
+ QueryWithDirective hero {
567
+ ...CharacterInfo @customFragmentSpreadDirective
568
+ }
569
+ }
570
+
571
+ **Inline fragment directives **:
572
+
573
+ Inline fragments also support directives using the
574
+ :meth: `directives <gql.dsl.DSLInlineFragment.directives> ` method::
575
+
576
+ query_with_directive = ds.Query.hero.args(episode=6).select(
577
+ ds.Character.name,
578
+ DSLInlineFragment().on(ds.Human).select(ds.Human.homePlanet).directives(
579
+ ds("@customInlineFragmentDirective")
580
+ )
581
+ )
582
+
583
+ This generates::
584
+
585
+ {
586
+ hero(episode: JEDI) {
587
+ name
588
+ ... on Human @customInlineFragmentDirective {
589
+ homePlanet
590
+ }
591
+ }
592
+ }
593
+
594
+ **Variable definition directives **:
595
+
596
+ You can also add directives to variable definitions using the
597
+ :meth: `directives <gql.dsl.DSLVariable.directives> ` method::
598
+
599
+ var = DSLVariableDefinitions()
600
+ var.episode.directives(ds("@customVariableDirective"))
601
+ # Note: the directive is attached to the `.episode` variable definition (singular),
602
+ # and not the `var` variable definitions (plural) holder.
603
+
604
+ op = DSLQuery(ds.Query.hero.args(episode=var.episode).select(ds.Character.name))
605
+ op.variable_definitions = var
606
+
607
+ This will generate::
608
+
609
+ query ($episode: Episode @customVariableDirective) {
610
+ hero(episode: $episode) {
611
+ name
612
+ }
613
+ }
614
+
615
+ Complete Example for Directives
616
+ """""""""""""""""""""""""""""""
617
+
618
+ Here's a comprehensive example showing directives on multiple locations:
619
+
620
+ .. code-block :: python
621
+
622
+ from gql.dsl import DSLFragment, DSLInlineFragment, DSLQuery, dsl_gql
623
+
624
+ # Create variables for directive conditions
625
+ var = DSLVariableDefinitions()
626
+
627
+ # Fragment with directive on definition
628
+ character_fragment = DSLFragment(" CharacterInfo" ).on(ds.Character).select(
629
+ ds.Character.name, ds.Character.appearsIn
630
+ ).directives(ds(" @fragmentDefinition" ))
631
+
632
+ # Query with directives on multiple locations
633
+ query = DSLQuery(
634
+ ds.Query.hero.args(episode = var.episode).select(
635
+ # Field with directive
636
+ ds.Character.name.directives(ds(" @skip" ).args(** {" if" : var.skipName})),
637
+
638
+ # Fragment spread with directive
639
+ character_fragment.spread().directives(
640
+ ds(" @include" ).args(** {" if" : var.includeFragment})
641
+ ),
642
+
643
+ # Inline fragment with directive
644
+ DSLInlineFragment().on(ds.Human).select(ds.Human.homePlanet).directives(
645
+ ds(" @skip" ).args(** {" if" : var.skipHuman})
646
+ ),
647
+
648
+ # Meta field with directive
649
+ DSLMetaField(" __typename" ).directives(
650
+ ds(" @include" ).args(** {" if" : var.includeType})
651
+ )
652
+ )
653
+ ).directives(ds(" @query" )) # Operation directive
654
+
655
+ # Variable definition with directive
656
+ var.episode.directives(ds(" @variableDefinition" ))
657
+ query.variable_definitions = var
658
+
659
+ # Generate the document
660
+ document = dsl_gql(character_fragment, query)
661
+
662
+ This generates GraphQL equivalent to::
663
+
664
+ fragment CharacterInfo on Character @fragmentDefinition {
665
+ name
666
+ appearsIn
667
+ }
668
+
669
+ query (
670
+ $episode: Episode @variableDefinition
671
+ $skipName: Boolean!
672
+ $includeFragment: Boolean!
673
+ $skipHuman: Boolean!
674
+ $includeType: Boolean!
675
+ ) @query {
676
+ hero(episode: $episode) {
677
+ name @skip(if: $skipName)
678
+ ...CharacterInfo @include(if: $includeFragment)
679
+ ... on Human @skip(if: $skipHuman) {
680
+ homePlanet
681
+ }
682
+ __typename @include(if: $includeType)
683
+ }
684
+ }
685
+
387
686
Executable examples
388
687
-------------------
389
688
@@ -399,4 +698,5 @@ Sync example
399
698
400
699
.. _Fragment : https://graphql.org/learn/queries/#fragments
401
700
.. _Inline Fragment : https://graphql.org/learn/queries/#inline-fragments
701
+ .. _Directives : https://graphql.org/learn/queries/#directives
402
702
.. _issue #308 : https://github.com/graphql-python/gql/issues/308
0 commit comments