Django/Graphene mutations doesn't apply - python

I am currently creating a GraphQL interface for my Django app, using Python-Graphene. While queries works great, mutations - not quite.
The model for Ingredient:
class Ingredient(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(editable=False)
needs_refill = models.BooleanField(default=False)
created = models.DateTimeField('created', auto_now_add=True)
modified = models.DateTimeField('modified', auto_now=True)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Ingredient, self).save(*args, **kwargs)
Here's the schema code (full code here: https://ctrlv.it/id/107369/1044410281):
class IngredientType(DjangoObjectType):
class Meta:
model = Ingredient
...
class CreateIngredient(Mutation):
class Arguments:
name = String()
ok = Boolean()
ingredient = Field(IngredientType)
def mutate(self, info, name):
ingredient = IngredientType(name=name)
ok = True
return CreateIngredient(ingredient=ingredient, ok=ok)
class MyMutations(ObjectType):
create_ingredient = CreateIngredient.Field()
schema = Schema(query=Query, mutation=MyMutations)
and my mutation:
mutation {
createIngredient(name:"Test") {
ingredient {
name
}
ok
}
}
Running it returns proper object and ok is True, but no data is pushed to the database. What should I do? What did I miss?

Close...inside your mutate method you need to save your ingredient as an instance of Ingredient (not IngredientType) and then use that to create your IngredientType object. Also, you should declare mutate as #staticmethod. Something like:
#staticmethod
def mutate(root, info, name):
ingredient = Ingredient.objects.create(name=name)
ok = True
return CreateIngredient(ingredient=ingredient, ok=ok)

Related

DRF Create new object and link to (but not create) it's related items (nested serializer)?

I'm trying to link multiple items (subdomains) to an item being created (evidence).
My form submits okay - but I'm trying to figure out the 'best' way to go about this.
According to the docs I have to override the create method - but their example shows them 'creating' the related objects.
I don't want to do that. I want to just just add those related items to the piece of evidence I am creating (to create the relationship)
Here are my serializers:
class SubdomainSerializer(serializers.ModelSerializer):
class Meta:
model = Subdomain
fields = [
"id",
"domain",
"short_description",
"long_description",
"character_code",
]
class EvidenceSerializer(serializers.ModelSerializer):
"""
"""
created_by = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
updated_by = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
absolute_url = serializers.SerializerMethodField()
created_by_name = serializers.SerializerMethodField()
updated_by_name = serializers.SerializerMethodField()
subdomains = SubdomainSerializer(many=True)
class Meta:
model = Evidence
fields = "__all__"
extra_kwargs = {
"subdomains": {
"error_messages": {
"null": "This field is required.",
"blank": "This field is required.",
}
},
}
def get_absolute_url(self, obj):
return obj.get_absolute_url()
def get_created_by_name(self, obj):
return obj.created_by.full_name
def get_updated_by_name(self, obj):
return obj.updated_by.full_name
def create(self, validated_data):
subdomains_data = validated_data.pop("subdomains")
evidence = Evidence.objects.create(**validated_data)
for subdomain in subdomains_data:
# This is where I want to add the subdomains to the evidence - not create ones - how best to accomplish that?
Subdomain.objects.create(**subdomain)
return evidence
def update(self, instance, validated_data):
# Add the requestor as the updater in a PATCH request
request = self.context["request"]
validated_data["updated_by"] = request.user
return super().update(instance, validated_data)
Is there a better way to setup my serializers? Should I be doing something different to make this more achievable?
Edited to add my models:
class Subdomain(CreateUpdateMixin):
"""
"""
domain = models.ForeignKey(Domain, on_delete=models.PROTECT)
short_description = models.CharField(max_length=100)
long_description = models.CharField(max_length=250)
character_code = models.CharField(max_length=5)
proficiency_levels = models.ManyToManyField(SubdomainProficiencyLevel)
class Meta:
verbose_name = "Subdomain"
verbose_name_plural = "Subdomains"
def __str__(self):
"""Unicode representation of Subdomain."""
return f"{self.character_code}"
class Evidence(CreateUpdateMixin, CreateUpdateUserMixin, SoftDeletionModel):
"""
"""
subdomains = models.ManyToManyField(Subdomain, related_name="evidences")
evaluation = models.ForeignKey(
Evaluation, related_name="evidences", on_delete=models.PROTECT
)
published = models.BooleanField(default=False)
comments = models.CharField(max_length=500)
class Meta:
ordering = ["-created_at"]
verbose_name = "Evidence"
verbose_name_plural = "Evidence"
def __str__(self):
"""Unicode representation of Evidence."""
return f"{self.subdomain} : {self.comments}"
def get_absolute_url(self):
"""Return absolute url for Evidence."""
return reverse("evidence-detail", args=[str(self.id)])
Here you want to set the subdomain ForeignKey to the new created evidence.
For that, you just have to retrieve the subdomain and update his evidence field like this :
def create(self, validated_data):
subdomains_data = validated_data.pop("subdomains")
evidence = Evidence.objects.create(**validated_data)
for subdomain in subdomains_data:
# Retrieve the subdomain and update the evidence attribute
sub_id = subdomain['id']
sub = Subdomain.objects.get(id=sub_id)
sub.evidence = evidence
sub.save()
return evidence
NB : I use the default inverse ForeignKey relationship : sub.evidence

Error initialising model with UUIDField django

Problem :
So, I have been trying to make an object of the model Trade with an initial value to the identifier from my custom function gen_rand_string().
But the problem is when, I am initialising it.
If I remove the initializer and set the UUIDField to NULL, it works out to be fine.
This is the error, I am getting :
TypeError at /admin/account/trade/add/
int() argument must be a string or a number, not 'Trade'
My Trade class :
class Trade(models.Model):
NEW_TRADE = 'N'
CANCELLED_TRADE = 'C'
PENDING_TRADE = 'P'
STATUS_OF_TRADE = (
(NEW_TRADE, "New"),
(CANCELLED_TRADE, "Cancelled"),
(PENDING_TRADE, "Pending")
)
TYPE_BUY = 'B'
TYPE_SELL = 'S'
TYPE_OF_TRADE = (
(TYPE_BUY, "Buy"),
(TYPE_SELL, "Sell")
)
user = models.OneToOneField('UserProfile', related_name='trades')
identifier = models.UUIDField(null=False, editable=False)
created_at = models.DateTimeField(auto_now_add=True, editable=False)
finished_at = models.DateTimeField(auto_now_add=True)
amount = models.DecimalField(max_digits=19, decimal_places=10, null=False)
type = models.CharField(max_length=2, choices=TYPE_OF_TRADE, null=False)
status = models.CharField(max_length=2, choices=STATUS_OF_TRADE, default=PENDING_TRADE, null=False)
def __init__(self, *args, **kwargs):
self.identifier = gen_rand_string()
super(Trade, self).__init__(self, *args, **kwargs)
class Meta:
ordering = ('created_at',)
def __unicode__(self):
return self.identifier
def __str__(self):
return self.identifier
My gen_rand_string() :
def gen_rand_string(purpose=''):
if purpose:
return purpose + '_' + get_random_string(length=64 - len(purpose))
else:
return get_random_string(length=64)
Suggestions :
I am making a random string for each trade in not a better way, would someone suggest somewhat better option, or something they would have it in their place.
I am using python 2.7 which is the reason of my initialisation of the object in a different way
Thanks.
You are discouraged from overriding __init__ for Django models. You use your gen_rand_string as the field's default:
identifier = models.UUIDField(null=False, editable=False, default=gen_rand_string)
However you probably don't need to define your own gen_rand_string method - just use uuid.uuid4, as in docs for UUIDField.
identifier = models.UUIDField(null=False, editable=False, default=uuid.uuid4)
problem is with your init function syntax.
def __init__(self, *args, **kwargs):
super(Trade, self).__init__(self, *args, **kwargs)
self.identifier = gen_rand_string()

How to create form which will show fields from two models?

I have two models:
class Product(models.Model):
name = models.CharField(verbose_name="name", max_length=40)
cost = models.FloatField(verbose_name="price")
def __unicode__(self):
return self.name
class Shop(models.Model):
product = models.ManyToManyField(Product)
name = models.CharField(verbose_name="Nazwa", max_length=40)
budget = models.FloatField(verbose_name="kwota")
def __unicode__(self):
return self.name
I created a form
code:
class ShopForm(forms.ModelForm):
product = forms.ModelMultipleChoiceField(queryset = Product.objects.all(), widget=forms.CheckboxSelectMultiple(),required=True)
name = forms.CharField(max_length=15, label='Name')
budget = forms.FloatField()
class Meta:
model = Shop
fields = ('product','name', 'budget')
It looks like on this picture, i don't know how can i show next to name of products their price? For example:
hehe - 12$
Now i have only name, i want get cost from model "Product", anybody know how can i do it?
You can solve this in different ways:
make changes in __unicode__ method in your model
class Product(models.Model):
...
def __unicode__(self):
return u"{}({})".format(self.name, self.cost)
override init method in form
class ShopForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ShopForm, self).__init__(*args, **kwargs)
self.fields['product'].label_from_instance = lambda obj: u"%s | %s" % (obj.name, obj.cost)
create custom form field:
from django.forms.models import ModelMultipleChoiceField
class ProductModelChoiceField(ModelMultipleChoiceField):
def label_from_instance(self, obj):
return u"%s | %s" % (obj.name, obj.cost)

Django: Creating editable default values for model instance based on foreignkey existance

I'm playing around in Django, and wondering if there is a way to loop through instances of two different models I have created?
/ models.py:
class Tran(models.Model):
name = models.CharField(max_length=300)
description = models.CharField(max_length=2000)
type = models.ForeignKey(TransactionType)
def __str__(self):
return self.name
class DocLink(models.Model):
trann = models.ForeignKey(Transaction)
t_link = models.CharField(max_length=2000)
t_display = models.CharField(max_length=1000)
p_display = models.CharField(max_length=300)
p_link = models.CharField(max_length=2000)
def __str__(self):
return self.link
What I want to do:
Look through each of the Tran instances and create a default value for the links/displays in the DocLink table instead of doing it manually.
Is there anyway I can be pointed in the right direction?
If you want to set links/displays default value in DocLink instance based on trann field you can override model's save method.
For example following code shows how to set t_link if it doesn't have a value:
class DocLink(models.Model):
trann = models.ForeignKey(Transaction)
t_link = models.CharField(max_length=2000)
t_display = models.CharField(max_length=1000)
p_display = models.CharField(max_length=300)
p_link = models.CharField(max_length=2000)
def __str__(self):
return self.link
def save(self, *args, **kwargs):
if not self.t_link:
pass # TODO set self.t_link based on trann
super(DocLink, self).save(*args, **kwargs)
Also you can change model's trann field to:
trann = models.ForeignKey(Transaction, related_name="doclinks")
And then access to all DocLinks of a Tran with:
# t is an instance of Tran class
t.doclinks.all()
So you can loop through this list and do what you want.

Django multiple similar models

I want refactor some of my code in models because it's a little mess. I have couple models.
class Part(models.Model):
class Category(models.Model):
class Labor(models.Model):
And so on, seven in total. I am generating for them ID. For Part it is:
def save(self, *args, **kwargs):
if not Part.objects.count():
latest = 'XXX00000'
else:
latest = Part.objects.all().order_by('-par_id')[0].par_id
self.par_id = "PAR" + str(int(latest[3:]) + 1).zfill(5)
super(Part, self).save(*args, **kwargs)
And it's pretty similar for rest of classes. Only name of class is changing, three letters identification and paramtere in order_by. I was wondering how can I do it DRY. Because it's 7 lines of code on each class that should be somehow shortened.
I was wondering maybe create BaseModel class inherited from it and somehow change only mentioned things. I would like to get some directions how can I do it better.
Edit:
class Part(models.Model):
par_id = models.CharField(primary_key=True, unique=True, max_length=9, blank=False)
par_name = models.CharField(max_length=50)
def save(self, *args, **kwargs):
if not Part.objects.count():
latest = 'XXX00000'
else:
latest = Part.objects.all().order_by('-par_id')[0].par_id
self.par_id = "PAR" + str(int(latest[3:]) + 1).zfill(5)
super(Part, self).save(*args, **kwargs)
class Category(models.Model):
cat_id = models.CharField(primary_key=True, unique=True, max_length=9)
cat_name = models.CharField(max_length=50)
def save(self, *args, **kwargs):
if not Category.objects.count():
latest = 'XXX00000'
else:
latest = Category.objects.all().order_by('-cat_id')[0].cat_id
self.cat_id = "CAT" + str(int(latest[3:]) + 1).zfill(5)
super(Category, self).save(*args, **kwargs)
That are two o my classes.
Inheriting is definitely a good idea.
You're not giving much information about the models. So there are 2 main options for inheriting models:
A) To use an AbstractModel which would hold the common fields and some common methods. And then use child models to extend the fields and methods as you need. Here is an example from the django docs:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
B) If you're only interested in inheriting or extending the behavioural parts of your models (like the different methods for generating the id's), a proxy model would be a better option. Take a look at the docs: https://docs.djangoproject.com/en/dev/topics/db/models/#proxy-models
Here is an example taken from the django docs:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
create class BaseModel(models.Model): and copypaste your save method there, but replace Part with self.__class__ , for example
class BaseModel(models.Model):
# some fields here
class Meta:
abstract = True
def save(self, *args, **kwargs):
first_declared_field = self.__class__._meta.fields[1].name
if self.__class__.objects.count():
latest = getattr(self.__class__.objects.order_by('-' + first_declared_field)[0], first_declared_field)
else:
latest = 'XXX00000'
field_value = first_declared_field.name.split('_')[0].upper() + str(int(latest[3:]) + 1).zfill(5)
setattr(self, first_declared_field, field_value)
super(BaseModel, self).save(*args, **kwargs)
class SomeChildModel(BaseModel):
pass

Categories