Extending query arguments in graphene/graphene_django - django-models

How do I add non-field arguments to a GraphQL query in graphene? Here's an example of a use case. I'd like to be able to do:
{
hsv(h: 40, s: 128, v: 54) {
r
g
b
name
}
with this Django model:
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator,
class Color(models.Model):
name = models.CharField(
"name",
max_length=24,
null=False, blank=False)
r = models.IntegerField(
"red", null=False, blank=False,
validators=[MinValueValidator(0), MinValueValidator(255)]
)
g = models.IntegerField(
"green", null=False, blank=False,
validators=[MinValueValidator(0), MinValueValidator(255)]
)
b = models.IntegerField(
"blue", null=False, blank=False,
validators=[MinValueValidator(0), MinValueValidator(255)]
)
and this GraphQL object type and Query based on it:
from graphene import ObjectType, IntegerField, Field, relay
from graphene_django import DjangoObjectType
from .django import Color
from colorsys import hsv_to_rgb
class ColorNode(DjangoObjectType):
r = IntegerField()
g = IntegerField()
b = IntegerField()
class Meta:
model = Color
class Query(ObjectType):
rgb = relay.node.Field(ColorNode)
hsv = relay.node.Field(ColorNode)
named = relay.node.Field(ColorNode)
def resolve_rgb(self, args, context, info):
if not all(map(lambda x: x in args, ['r', 'g', 'b'])):
# Arguments missing
return None
return Color.objects.get(**args)
def resolve_hsv(self, args, context, info):
if not all(map(lambda x: x in args, ['h', 's', 'v'])):
# Arguments missing
return None
r, g, b = hsv_to_rgb(args['h'], args['s'], args['v'])
return Color.objects.get(r=r, g=g, b=b)
def resolve_named(self, args, context, info):
if not 'name' in args:
# Arguments missing
return None
return Color.objects.get(name=args['name'])
It fails because the arguments aren't accepted. What am I missing?

The answer turns out to be simple. To add arguments to the resolver, declare the arguments in the constructor of the field, like this:
rgb = relay.node.Field(ColorNode,
r=graphene.String(),
g=graphene.String(),
b=graphene.String())
hsv = relay.node.Field(ColorNode,
h=graphene.String(),
s=graphene.String(),
v=graphene.String()))
named = relay.node.Field(ColorNode,
name=graphene.String())
The arguments may then be included in the query, as shown above.

Related

Count the no of movies in django rest framework

I want to count the no of movies worked by the actors in api. i have tried this but not working
models.py
class Actor(models.Model):
actors = models.CharField(max_length=100)
class Genre(models.Model):
genre = models.CharField(max_length=100)
class Movie(models.Model):
name = models.CharField(max_length=100)
actors = models.ManyToManyField(Actor, related_name="actor_movies")
genre = models.ManyToManyField(Genre,related_name="genre_movies")
serializers.py
class ActorSerializer(serializers.HyperlinkedModelSerializer):
actor_movies = serializers.RelatedField(source='actor.movies', read_only = True)
class Meta:
model = Actor
fields = ['id','actors','actor_movies']
class GenreSerializer(serializers.HyperlinkedModelSerializer):
genre_movies = serializers.RelatedField(source='genre.names', read_only = True)
class Meta:
model = Genre
fields = ['id','genre','genre_movies']
class MovieSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Movie
fields = ['url','name','actors','genre']
im getting output like this
[
{
"id": 1,
"genre": "Ajith"
},
{
"id": 2,
"genre": "Vijay"
}
]
in actual output i need the total no of movies he worked also.
You can use a SerializerMethodField:
class ActorSerializer(serializers.HyperlinkedModelSerializer):
count_actor_movies= serializers.SerializerMethodField()
def get_count_actor_movies(self, instance):
return instance.actor_movies.count()
class Meta:
model = Actor
fields = ['id','actors','count_actor_movies']
For more details, the official docs are here.

Removing Null nested fields in Django Serializer --- or how to iterate with Null?

This issue I am having is likely a React iteration problem but I think taking care of it at the root inside Django will be more efficient instead of on the front end.
Possible remedies:
ternary operation to conditionally display objects and parameters if not null
remove null fields from response to avoid needing to do ternary operation through django itself
Although, in the first place, I cannot seem to be able to iterate through my response.
I have a double nested serializer:
Models:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class VideoProduct(models.Model):
...
class ImageProduct(models.Model):
...
class ProfileProduct(models.Model):
profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
video_product = models.ForeignKey(VideoProduct, on_delete=models.CASCADE, blank=True)
image_product = models.ForeignKey(VideoProduct, on_delete=models.CASCADE, blank=True)
class Meta:
unique_together = ('profile', 'video_product')
unique_together = ('profile', 'image_product')
Views.py:
class VideoProductViewSet(viewsets.ModelViewSet):
queryset = VideoProduct.objects.all()
serializer_class = VideoProductSerializer
class ImageProductViewSet(viewsets.ModelViewSet):
queryset = ImageProduct.objects.all()
serializer_class = VideoProductSerializer
class ProfileProductsViewSet(viewsets.ModelViewSet):
queryset = ProfileProduct.objects.all()
serializer_class = ProfileProductsSerializer
class ProfileBySlug(generics.ListAPIView):
serializer_class = ProfileBySlugSerializer
def get_queryset(self):
slug = self.request.query_params.get('slug', None)
queryset = Profile.objects.filter(slug=slug)
if slug is not None:
queryset = queryset.filter(slug=slug)
return queryset
Serializers:
class VideoProductSerializer(serializers.ModelSerializer):
class Meta:
model = VideoProduct
fields = ['id', 'price']
class ImageProductSerializer(serializers.ModelSerializer):
class Meta:
model = ImageProduct
fields = ['id', 'price']
class ProfileProductsSerializer(serializers.ModelSerializer):
video_product = VideoProductSerializer(read_only=True)
image_product = ImageProductSerializer(read_only=True)
class Meta:
model = ProfileProduct
fields = ['id', 'video_product', 'image_product']
class ProfileBySlugSerializer(serializers.ModelSerializer):
profile_products_set = serializers.SerializerMethodField('get_profile_products')
def get_profile_products(self, profile):
qs = ProfileProduct.objects.filter(profile=profile)
serializer = ProfileProductsSerializer(instance=qs, many=True)
return serializer.data
class Meta:
model = Profile
fields = ['id', 'title', 'description', 'slug', 'image', 'status', 'profile_products_set']
The issue is that a ProfileProduct will only have one association to either a video_product or image_product but my responses are like this:
API Request:
axios.get(`http://127.0.0.1:8000/api/v1/profile/`, {
params: {
slug: this.state.slug,
}
})
.then(res => {
this.setState({ profile: res.data.results[0] });
this.setState({ products: res.data.results[0].profile_products_set });
....
Entire response:
[{…}]
0:
description: "Description"
id: 1
profile_products_set: Array(2)
0:
video_product: {id: 1, price: "0.00", status: true}
image_product: null
__proto__: Object
1: {image_product: {…}, video_product: null}
length: 3
__proto__: Array(0)
title: "Admin Title"
__proto__: Object
length: 1
__proto__: Array(0)
I set the state for profile_products_set with:this.setState({ products: res.data.results[0].profile_products_set })
Now when I go to iterate within React:
{ this.state.products.map(product =>
<div>
<li>{product.id}</li>
<li>{product.price}</li>
</div>
)}
It is blank with only bullet points.
As mentioned above, how can I one iterate over this and display it on the front end, and then if possible, remove the null fields from the response all together to avoid needing to do and ternary operations based on the product type
You can use to_representation to override the serialization. You can then remove the entries with None value.
class ProfileProductsSerializer(serializers.ModelSerializer):
video_product = VideoProductSerializer(read_only=True)
image_product = ImageProductSerializer(read_only=True)
class Meta:
model = ProfileProduct
fields = ['id', 'video_product', 'image_product']
def to_representation(self, instance):
data = super().to_representation()
return {k: v for k, v in data.items() if v is not None}

How to count deep relationship fields with Django?

How could I count the relationship of the models below?
class Client(models.Model):
name = models.CharField(max_length=150)
class Pet(models.Model):
client = models.ForeignKey(Client, related_name='pet')
name = models.CharField(max_length=150)
class Photo(models.Model):
pet = models.ForeignKey(Pet, related_name='photo')
photo = models.ImageField(storage=DIR)
I would like to return the total pet and total photos taken of all pets dr to a single customer client, with the following answer:
[
{
"name": "john",
"totalPet": 3, // imagine 10 photos were taken of each pet
"totalPhoto": 30,
},
{
"name": "Mary",
"totalPet": 2, // imagine that 10 photos were taken of one and 5
// of another pet
"totalPhoto": 15,
},
]
My serializer is as follows:
class ClientSerializer(ModelSerializer):
totalPet = serializers.SerializerMethodField()
def get_totalPet(self, obj):
return obj.pet.count()
class Meta:
model = Client
fields = ('name', 'totalPet')
As can be observed, I can total the amount of pets for each customer, but I can't find a way to total the photos of all pets of a customer.
What would be the best way to do this photo count?
You can perform a filter on Photo model with your client.pet queryset, using the __in lookup.
At first, I would define a custom QuerySet for our Photo model:
from django.db import models
class PhotoQuerySet(models.QuerySet):
def for_client(self, client):
# Get all related pets to the current client
client_pets = client.pet.all()
# Filter Photos that are linked with these pets
return self.filter(pet__in=client_pets)
And then inside the model:
class Photo(models.Model):
...
objects = PhotoQuerySet.as_manager()
This will allow you to do the following: Photo.objects.for_client(client=...)
After that, you can use this method in the serializer:
class ClientSerializer(ModelSerializer):
total_pets = serializers.SerializerMethodField()
total_photos = serializers.SerializerMethodField()
def get_total_pets(self, obj):
return obj.pet.count()
def get_total_photos(self, obj):
# Calling our newly created method and aggregating count
return Photo.objects.for_client(client=obj).count()
class Meta:
model = Client
fields = ('name', 'total_pets', 'total_photos')

How to update the sql server's identity column using play2.6 slick3?

I got the following error when i try to run the sbt scala project using sql server,
[SQLServerException: Cannot update identity column 'ID'.]
I am using Play 2.6,Scala 2.12, Slick 3
my update function is,
def update(id: Long): Action[AnyContent] = Action.async { implicit request =>
topicForm.bindFromRequest.fold(
formWithErrors => Future.successful(BadRequest(html.editForm(id, formWithErrors))),
topic => {
val futureTopUpdate = dao.update(id, topic.copy(id = Some(id)))
futureTopUpdate.map { result =>
Home.flashing("success" -> "Topic %s has been updated".format(topic.code))
}.recover {
case ex: TimeoutException =>
Logger.error("Problem found in topic update process")
InternalServerError(ex.getMessage)
}
})
}
and the DAO:
override def update(id: Long, topic: Topic): Future[Int] =
try db.run(filterQuery(id).update(topic))
finally db.close
any idea?
You can show the filterQuery(id) implementation, something similar in a dao of us works well in this way:
override def update(id: Long, topic: Topic): Future[Int] = {
db.run(filterQuery(id).update(topic.copy(id))
}
Notice: topic.copy(id)
And filterQuery is:
def filterQuery(id: Int) = themes.filter(_.id === id)
We using Play 2.6,Scala 2.12, Slick 3 with MYSQL.
Update # 1:
-> Entity:
case class CategoryRow(id: Int, name: String, description: String)
-> Mapping:
trait CategoryMapping {
self: HasDatabaseConfigProvider[JdbcProfile] =>
import dbConfig.profile.api._
private[models] class CategoryTable(tag: Tag)
extends Table[CategoryRow](tag, "category") {
def id = column[Int]("id", O.AutoInc, O.PrimaryKey)
def name = column[String]("name", O.Length(TextMaxLength_250))
def description = column[String]("description", Nullable)
def categoryNameAgencyIndex = index("categoryName_agency_idx", (name, agencyId), unique = true)
override def * = (
id,
name,
description
) <> (CategoryRow.tupled, CategoryRow.unapply)
}
private[models] val Categories = TableQuery[CategoryTable]
private[models] val CategoriesInsertQuery = Categories returning Categories.map(_.id)
}
-> REPO
trait CategoryRepository {
//...
def update(id: Int, category: Category)(implicit agencyId: Int): Future[Int]
//...
}
-> REPOImpl:
#Singleton
class CategoryRepositoryImpl #Inject()(protected val dbConfigProvider: DatabaseConfigProvider)(implicit ec: RepositoryExecutionContext)
extends CategoryRepository with HasDatabaseConfigProvider[JdbcProfile] with CategoryMapping {
import dbConfig.profile.api._
//....
def update(id: Int, category: CategoryRow)(implicit agencyId: Int): Future[Int] =
db.run(filter(id).update(category.copy(id)))
private def filter(id: Int) = Categories.filter(_.id === id)
//....
}
-> RepositoryExecutionContex
class RepositoryExecutionContext #Inject()(actorSystem: ActorSystem) extends CustomExecutionContext(actorSystem, "repository.dispatcher")
and aplication.conf:
# db connections = ((physical_core_count * 2) + effective_spindle_count)
fixedConnectionPool = 5
repository.dispatcher {
executor = "thread-pool-executor"
throughput = 1
thread-pool-executor {
fixed-pool-size = ${fixedConnectionPool}
}
}
There's some more information about fold in Chapter 3.3 of Essential Slick.

create a class Table for an inexisting table slick scala (Slick 3.0.0, scala)

Assume that we have a database which contains two tables: Coffee and Suppliers and we have their corresponding case classes and tables, just as in the documentation:
import scala.slick.driver.MySQLDriver.simple._
import scala.slick.lifted.{ProvenShape, ForeignKeyQuery}
// A Suppliers table with 6 columns: id, name, street, city, state, zip
class Suppliers(tag: Tag) extends Table[(Int, String, String, String, String, String)](tag, "SUPPLIERS") {
def id: Column[Int] = column[Int]("SUP_ID", O.PrimaryKey) // This is the primary key column
def name: Column[String] = column[String]("SUP_NAME")
def street: Column[String] = column[String]("STREET")
def city: Column[String] = column[String]("CITY")
def state: Column[String] = column[String]("STATE")
def zip: Column[String] = column[String]("ZIP")
// Every table needs a * projection with the same type as the table's type parameter
def * : ProvenShape[(Int, String, String, String, String, String)] = (id, name, street, city, state, zip)
}
// A Coffees table with 5 columns: name, supplier id, price, sales, total
class Coffees(tag: Tag) extends Table[(String, Int, Double, Int, Int)](tag, "COFFEES") {
def name: Column[String] = column[String]("COF_NAME", O.PrimaryKey)
def supID: Column[Int] = column[Int]("SUP_ID")
def price: Column[Double] = column[Double]("PRICE")
def sales: Column[Int] = column[Int]("SALES")
def total: Column[Int] = column[Int]("TOTAL")
def * : ProvenShape[(String, Int, Double, Int, Int)] = (name, supID, price, sales, total)
// A reified foreign key relation that can be navigated to create a join
def supplier: ForeignKeyQuery[Suppliers, (Int, String, String, String, String, String)] =
foreignKey("SUP_FK", supID, TableQuery[Suppliers])(_.id)
}
Now assume that we want to do a join:
val result = for {
c <- coffees
s <- suppliers if c.supID === s.id
} yield (c.name, s.name)
And here dealing with the result is complicated ( and it's more complicated if we have a lot of joins) because we need always to remember the order of names, to know what _._1 or _._2 refer to ... etc.
Question 1 Is there a way to change the type of the result as a table of a new class which contains the desired columns ?
Question 2 Here is a way but I can't finish it, we construct a case class for example:
case class Joined(nameS: String,nameC: String)
and after that we construct the corresponding table which I don't know how
class Joineds extends Table[Joinedclass] {
//Todo
}
and when we write a join we can write something like ( so that we can transform result to a type Joined) :
val result = for {
c <- coffees
s <- suppliers if c.supID === s.id
} yield (c.name, s.name).as(Joinds)
Thank you.
Can you define it like:
val result = for {
c <- coffees
s <- suppliers if c.supID === s.id
} yield Joined(c.name, s.name)
And the tuck it away in some convenient place?

Resources