User defined fields in graphQL - graphql

Making an employee management system with graphene-Django and I have a JsonB field that I don't know the shape of because each company will defined the shape.
Here's the model:
class Employee(models.Model):
bonuses = models.JSONField(default=dict)
#...OTHER FIELDS
here's the type:
class EmployeeType(DjangoObjectType):
class Meta:
model = Employee
fields = "__all__"
Here's the mutation class:
class EmployeeInfo(graphene.Mutation):
employee = graphene.List(EmployeeType)
class Arguments:
bonuses = GenericScalar()
#...OTHER FIELDS
def mutate(self, info, **kwargs):
#DOING STUFF
return EmployeeInfo(employee=employee)
Now, say a company wants to give bonuses to a developer with the following schema:
export const EMPLOYEE_INFO = gql`
mutation MutateEmployee(
$employeeOfTheMonth: Int
$mostPR: Int
$profitSharing: Int
) {
employeeInfo(
bonuses:{
employeeOfTheMonth: $employeeOfTheMonth
mostPR: $mostPR
profitSharing: $profitSharing
}
#...OTHER FIELDS
){....}
}
`
This is my current setup, the problem is I get only null values in the database for the bonuses field. Notice that I'm using GenericScalar which is not documented and I don't know if that's the wrong scalar.
If this is a restaurant, obviously the bonuses will be different and that's why I need a setup like this.
How can I define a field that will take user defined shapes?

I think this is what you need
class EmployeeInfo(graphene.Mutation):
employee = graphene.List(EmployeeType)
class Arguments:
bonuses = graphene.JSONString()
#...OTHER FIELDS

Related

How to order the id in serializers in Django Rest Framework?

when I try to sort my id in my serializers it gives me an error : AttributeError: 'int' object has no attribute 'all'.
Here is the code of my serializers :
class DatasetPidTrierSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.SerializerMethodField()
class Meta:
model = Dataset
fields = ['id']
def get_id(self, instance):
songs = instance.id.all().order_by('id')
return DatasetPidSerializer(songs, many=True).data
Here is the beginning of it : model Dataset
thank you for your help
First, you don't need to set id field in Dataset model. Django will do that automatically for you. You can define just two fields pid and iddatta_resolution.
class Dataset(models.Model):
pid = models.ManyToManyField(
...
)
iddata_resolution = models.ForeignKey(
...
)
And then in the serializer, you don't need to define id field. Because the default order will be id field automatically by Django.
class DatasetPidTrierSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Dataset
fields = ['id']

Implementing hierarchical DB tables with InheritanceType.JOINED and a `tenant_id` column in every table

I'm trying to implement a hierarchical structure using the InheritanceType.JOINED approach to store data in hierarchical DB tables. One caveat is that in our multi-tenant solution, a tenant_id column needs to be present on every table (for security and legal export reasons), even though this is redundant in some cases. This requirement is fixed.
Issue is that when inserting data, the query Hibernate generates does not fill in the tenant_id on the parent- and childtable, causing a constraint error.
The tables in the DB would look like this:
Code for the abstract vehicle entity:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
abstract class Vehicle(
var tenantId: Int,
var title: String
)
Car entity:
#Entity
class Car(
override var tenantId: Int,
override var title: String,
) : Vehicle(tenantId, title) {
var numberOfDoors: Int
}
Plane entity:
#Entity
class Plane(
override var tenantId: Int,
override var title: String,
) : Vehicle(tenantId, title) {
var numberOfPropellers: Int
}
When inserting a new Car in the database, Hibernate generates 2 queries, where the tenant_id is only added to the abstract Vehicle table:
insert into vehicle (tenant_id, title) values (?, ?)
insert into car (id, number_of_doors) values (?, ?)
Is there a way to instruct Hibernate to fill in the column on both tables?
One hack I've found is to implement a "second" tenantId variable on the class and specify a column explicitly, as such:
#Column(name = "tenant_id")
private val _tenantId: Int = tenantId
But it's not very clean and a neater solution would be nice.
Specifically in my case where the tenant_id column is a database setting, defining a computed default value on the tenant_id db column also works as a workaround:
current_setting('app.current_tenant', true)::INT

How to save partial data in a separate model and then query checks if that data exists?

My data consists of some products, which are defined in FdProduct model:
# models.py:
class FdProduct(models.Model):
product_id = models.CharField(max_length=10, primary_key=True)
name = models.CharField(max_length=40)
active = models.BooleanField(default=True)
def __str__(self):
return self.product_id
# serializers.py:
class FdProductSerializer(serializers.ModelSerializer):
product_id = serializers.RegexField(regex='^\d{3}\.\d{6}$', max_length=10, min_length=10, allow_blank=False)
name = serializers.CharField(min_length=6, max_length=50, allow_blank=False)
class Meta:
model = FdProduct
fields = '__all__'
For each existing product I can prepare a configuration and save it using a model called SavedConfiguration:
# models:
class SavedConfiguration(models.Model):
saved_conf_id = models.CharField(max_length=13, primary_key=True)
saved_config = models.TextField()
product = models.ForeignKey(FdProduct, on_delete=models.CASCADE, default=0)
session_id = models.CharField(max_length=40, default=0)
creation_date = models.DateTimeField(auto_now=False, auto_now_add=True)
def __str__(self):
return str(self.saved_conf_id)
# serializers.py:
class SavedConfigurationSerializer(serializers.ModelSerializer):
saved_conf_id = serializers.RegexField(regex='^sc\d{2}', allow_blank=False)
saved_config = serializers.CharField(min_length=6)
session_id = serializers.RegexField(regex='^se\d{2}', allow_blank=False)
class Meta:
model = SavedConfiguration
fields = '__all__'
By connecting the SavedConfiguration with FdProduct model with use of ForeignKey I ensure that a product exists in the database when I configure it and want to save the configuration.
I'd like to introduce two things more: the first one would be a model for storing just a product_id and an array containing all saved_conf_ids for that product:
# models.py:
class ConfigOptions(models.Model):
product_id = ...
saved_conf_id = [...]
For example, if I configured a couple of times two products, Product One and Product Three, I may have data like this:
- Product One:
- C_ID_0023
- C_ID_0025
- C_ID_0032
- Product Three:
- C_ID_0149
- C_ID_0273
My question now is, how to construct such model and serializer for which records are created (copied) into ConfigOptions model table each time SavedConfiguration is saved?
Second question: I'm thinking about creating another model, say ConfigPresenceCheck, which would receive POST requests and based on that would check if saved product configurations exist (so, fetching them from ConfigOptions or returning 404), and if they exist, would return them together with all parameters from SavedConfiguration (e.g. saved_config, session_id, etc.).
Please give me directions how to build such models. I'd also appreciate some good tutorials related to
constructing Django models.
I think you should use many to many fields instead of foreign key, from my understanding many products can have many saved configurations and vice versa, at database level many to many fields are stored by creating a table any way.

How to post data to Embedded document with Mongoengine REST

I am trying to use Django with mongoengine to make an API.
So far I can get the objects and delete them. but when I want to post some data. Lets say student + course it is giving an error:
type object 'Course' has no attribute 'objects'
Models en ..
#Model.py
class Course(EmbeddedDocument):
course_name = StringField(max_length=200)
course_fee = StringField(max_length=200)
class Student(Document):
student_name = StringField(max_length=200)
student_contactperson = StringField(max_length=200)
student_adress = StringField(max_length=200)
courses = ListField(EmbeddedDocumentField(Course))
#Serializers.py
class CourseSerializer(EmbeddedDocumentSerializer):
class Meta:
model = Course
fields = ('course_name','course_fee')
class StudentSerializer(DocumentSerializer):
courses = CourseSerializer(many=True)
class Meta:
model = Student
fields = ('student_name','student_contactperson','student_adress','courses')
depth = 2
def create(self, validated_data):
course_data = validated_data.pop('courses')
student = Student.objects.create(**validated_data)
Course.objects.create(student=student, **course_data)
return student
#Views.py
class StudentViewSet(meviewsets.ModelViewSet):
lookup_field = 'name'
queryset = Student.objects.all().order_by('-date_joined')
serializer_class = StudentSerializer
A Document represents a MongoDB document (i.e a record in a collection), a Document class is bound to a particular collection. An EmbeddedDocument represents a structure that gets nested in a Document.
So by design an EmbeddedDocument isn't attached to any collection unless you embed it inside a Document.
This means that you can't query or save an EmbeddedDocument class, you need to query/save the parent Document.
Document.objects is an entry point for querying a collection, it only exists on Document classes. You are calling Course.objects.create but Course is an EmbeddedDocument.
I believe you need to change your code to the following
class StudentSerializer(DocumentSerializer):
...
def create(self, validated_data):
course_data = validated_data.pop('courses')
course = Course(**course_data) # assuming course_data is {course_name: ..., course_fee: ...}
return Student.objects.create(courses=[course], **validated_data)

GraphQL circular queries with one to many relationships

I'm learning GraphQL by building a simple python application, basically runs nmap scans stores output to a database, and can be queried by a GraphQL API. I seem to be a bit confused on how GraphQL works.
I have a few tables that are one-to-many relationships: user has many scans, scans have results, results have hosts, hosts have ports, hosts have os. Which I defined using sqlalchemy and used graphene
Now, in my GraphQL schema I have:
class Scans(SQLAlchemyObjectType):
class Meta:
model = ScansModel
class ScanResult(SQLAlchemyObjectType):
class Meta:
model = ScanResultModel
class Hosts(SQLAlchemyObjectType):
class Meta:
model = HostInfoModel
class Ports(SQLAlchemyObjectType):
class Meta:
model = PortInfoModel
class Os(SQLAlchemyObjectType):
class Meta:
model = OsInfoModel
class Query(graphene.ObjectType):
user = graphene.Field(User)
scans = graphene.List(Scans)
scan_results = graphene.List(ScanResult)
hosts = graphene.List(Hosts)
ports = graphene.List(Ports)
os = graphene.Field(Os)
def resolve_scans(self, info):
query = Scans.get_query(info)
return query
Now, when I make a GraphQL query, I can query scans, results, hostinfo, portinfo, osinfo, without having to have resolvers for those fields. I was under the impression that each of those fields would need a resolver.
Furthermore, I seem to be able to do circular quereis (so from scanresults I can query scans and from scans I can query user) due to the foreign keys and relationship table.
Is this the correct behaviour, or am misunderstanding how GraphQL works?
What you need to do is:
class ScanResult(SQLAlchemyObjectType):
class Meta:
model = ScanResultModel
scans = graphene.List(Scans, description="Scans from results.")
def resolve_scans(self, info):
query = Scans.get_query(info)
return query.filter(ScansModel.my_id == self.scans_id).all()
This will probably enables you to build queries like:
{
scanresult{
edges {
node {
id
scans{
id
}
}
}
}
I know that resolvers for each field with SQLAlchemyObjecType are handled inside the library.
when I use mongoengine without using MongoengineObjectType, I code like this.
class Query(graphene.ObjectType):
department = graphene.List(of_type=DepartmentField,
name=graphene.String(default_value="all"))
role = graphene.List(of_type=RoleField,
name=graphene.String(default_value="all"))
employee = graphene.List(of_type=EmployeeField,
name=graphene.String(default_value="all"))
def resolve_department(self, info, name):
if name == "all":
department = [construct(DepartmentField, object) for object in DepartmentModel.objects]
return department
else:
department = DepartmentModel.objects.get(name=name)
return [construct(DepartmentField, department)]
def resolve_role(self, info, name):
.
.
.

Resources