graphql-django update mutation with optional fields idiom? - django

When implementing GraphQL update mutations with many (here just a and b) optional InputObjectType fields it generates a lot of boilerplate to check if the InputObjectTypes fields have been passed. Is there some idiom which is considered best practice w.r.t. this topic?
# <app>/models.py
from django.db import models
class Something(models.Model):
a = models.CharField(default='')
b = models.CharField(default='')
# <app>/schema.py
import graphene
from graphene_django import DjangoObjectType
from .models import Something
class SomethingType(DjangoObjectType):
class Meta:
model = Something
class SomethingInput(graphene.InputObjectType):
# all fields are optional
a = graphene.String()
b = graphene.String()
class SomethingUpdateMutation(graphene.Mutation):
class Arguments:
id = graphene.ID(required=True)
something_data = SomethingInput(required=True)
something = graphene.Field(SomethingType)
def mutate(self, info, id, something_data):
something_db = Something.objects.get(pk=id)
# checking if fields have been passed or not and
# only change corresponding db value if value has been passed
if something_data.a is not None:
something_db.a = something_data.a
if something_data.b is not None:
something_db.b = something_data.b
something_db.save()
return SomethingUpdateMutation(something=something)
class Mutation(object):
# project schema inherits from this class
something_update_mutation = SomethingUpdateMutation.Field()

You can do this:
def mutate(self, info, **kwargs):
user = info.context.user
for k, v in kwargs.items():
user.k = v
user.save()
return UpdateUser(user=user)
But still, I'm also finding some elegant way to do it. 🤘
But, if user wanna remove the content from the field, then what will you do since you are checking !None. IMO, the best practice, when we update a data we send all the data with the updated fields. So we don't need to check, is something empty or not.

Related

Duplicate UUID if we enter a new input from html?

getting error- i saved one form from html into my database
while when i tried saving the next it gave me this error-
IntegrityError at customer
(1062, "Duplicate entry 'ad138e46-edc0-11va-b065-a41g7252ecb4'' for key 'customer.PRIMARY'")
kindly explain my uuid in models.py is -
class customer(models.Model):
customerid = models.CharField(default=str(uuid.uuid4()), max_length=500, primary_key=True)
customername=models.CharField(max_length=100)
kindly help
Updated
Form.py
class createcustomerform(ModelForm):
class Meta:
model=customer
fields=[
'customername']
Updated Models.py
import uuid
from uuid import UUID
from django.contrib.auth.models import User
from django.dispatch.dispatcher import receiver
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import pre_save
class customer(UUIDMixin,models.Model):
customername=models.CharField(max_length=100)
def __str__(self):
return self.customername
class UUIDMixin(models.Model):
uuid = models.UUIDField(blank=True,db_index=True,default=None,help_text=_('Unique identifier'),max_length=255,null=True,unique=True,verbose_name=_('UUID'))
class Meta:
abstract = True
#classmethod
def check_uuid_exists(cls, _uuid):
#Determine whether UUID exists """
manager = getattr(cls, '_default_manager')
return manager.filter(uuid=_uuid).exists()
#classmethod
def get_available_uuid(cls):
#Return an Available UUID """
row_uuid = uuid.uuid4()
while cls.check_uuid_exists(uuid=row_uuid):
row_uuid = uuid.uuid4()
return row_uuid
#receiver(pre_save)
def uuid_mixin_pre_save(sender, instance, **kwargs):
if issubclass(sender, UUIDMixin):
if not instance.uuid:
manager = getattr(instance.__class__, '_default_manager')
use_uuid = uuid.uuid4()
while manager.filter(uuid=use_uuid):
use_uuid = uuid.uuid4()
instance.uuid = use_uuid
#Automatically populate the uuid field of UUIDMixin models if not already populated.
You should use a proper UUIDField and avoid setting a default value.
Make sure the value is set when the object is created, and also ensure that the value is unique - obviously the chance of duplication in a UUID is incredibly small which is the whole point.
You could create yourself a model mixin which will add a uuid to your model(s) and ensure that the value is set when the object is saved;
class UUIDMixin(models.Model):
"""
Mixin for models contain a unique UUID, gets auto-populated on save
"""
uuid = models.UUIDField(
blank=True,
db_index=True,
# default=uuid.uuid4,
# NB: default is set to None in migration to avoid duplications
default=None,
help_text=_('Unique identifier'),
max_length=255,
null=True,
unique=True,
verbose_name=_('UUID'),
)
class Meta:
"""Metadata for the UUIDMixin class"""
abstract = True
#classmethod
def check_uuid_exists(cls, _uuid):
""" Determine whether UUID exists """
manager = getattr(cls, '_default_manager')
return manager.filter(uuid=_uuid).exists()
#classmethod
def get_available_uuid(cls):
""" Return an Available UUID """
row_uuid = uuid.uuid4()
while cls.check_uuid_exists(uuid=row_uuid):
row_uuid = uuid.uuid4()
return row_uuid
#receiver(pre_save)
def uuid_mixin_pre_save(sender, instance, **kwargs):
"""
Automatically populate the uuid field of UUIDMixin models if not already
populated.
"""
if issubclass(sender, UUIDMixin):
if not instance.uuid:
manager = getattr(instance.__class__, '_default_manager')
use_uuid = uuid.uuid4()
while manager.filter(uuid=use_uuid):
use_uuid = uuid.uuid4()
instance.uuid = use_uuid
Based on your comment, let me explain more.
The above is an abstract model, meaning it doesn't create a table itself, it can just be used by other (concrete) models so that they can use what it defines.
It's a benefit of classes & inheritance. Allowing you to not duplicate code on things that will be useful on many models.
No, in the meta you'll see abstract = True. So you define your models like MyModel(UUIDMixin, models.Model) and it gets a uuid field from this abstract model along with whatever you define.
You would use it by doing something like this;
class Customer(UUIDMixin, models.Model):
name = models.CharField(max_length=100)
If you really want to use the UUID as a primary key, maybe setup two mixins, a PrimaryUUIDMixin and the UUIDMixin, because on the whole a smaller value on the primary key may be more efficient.
You also mentioned Undefined variable: _
Typically in django you'd see an import at the top of the file like this;
from django.utils.translation import ugettext_lazy as _
This is then used to wrap strings so that they can be translated, for example, _("Hello")
Even if you don't translate your project, it's common to just include this anyway. You can read up on that here; https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#standard-translation

2 Users in one model

New to Django. I'm trying to develop a Feedback module, but designing a database structure makes me confused for various reasons:
Where do I need to store feedback_score (positive/neutral/negative feedback ratio), should it be put in custom User, or somewhere else?
How should I get the recipient of the feedback credentials, should it be passed by URL, how to link recipient to FeedbackModel in class-based-views?
How to feedback_score to be calculated each time for every User?
models.py
User = get_user_model()
# Create your models here.
class FeedbackModel(models.Model):
id = models.AutoField(primary_key=False, db_column='id')
sender = models.ForeignKey(
User,
on_delete=models.CASCADE,
primary_key=False, related_name='feedback_left_by')
# recipient = models.ForeignKey(
# User,
# on_delete=models.CASCADE,
# primary_key=True)
FEEDBACK_OPTION = (
(-1, 'Negative'),
(0, 'Neutral'),
(+1, 'Good'),
)
feedback = models.IntegerField(choices=FEEDBACK_OPTION)
opinion = models.CharField(max_length=255)
class Meta:
ordering = ['left_by', '-id']
urls.py
from django.urls import path
from . import views
app_name = 'feedback'
urlpatterns = [
path('leave_feedback/<str:left_to>/',
views.leave_feedback.as_view(), name='leavefeedback'),
]
views.py
from django.shortcuts import render, reverse
from django.views.generic import TemplateView, CreateView
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from .models import FeedbackModel
decorators = [login_required]
#method_decorator(decorators, name='dispatch')
class leave_feedback(CreateView):
model = FeedbackModel
fields = ['feedback', 'opinion']
success_url = '/'
#template_name = "feedback/leave_feedback.html"
def form_valid(self, form):
form.instance.sender = self.request.user
# ?
return super().form_valid(form)
custom user
class User(AbstractUser):
(...)
#property
def feedback_score(self)
return ???
feedback_score should be a class method in FeedbackModel, not a field. In general, you do not want fields in your database that can directly be derived from other fields. For instance:
class Person(models.Model):
name = models.CharField()
birthdate = models.DateField()
zodiac_sign = models.CharField()
If i have the birth date, I can easily get the zodiac sign. So, zodiac_sign should be a method. The same applies to your case. Just create a method that returns summary information on the feedback.
Where do I need to store feedback_score (positive/neutral/negative feedback ratio), should it be put in custom User, or somewhere else?
I think you current Model design is okay with the requirement. Just fix the ordering in class Meta.
BTW, if you want the user to have only one feedback, then consider using OneToOneField. If you want ratio of Feedback, just do like this:
feedbacks = Feeback.objects.all()
positive_feedbacks = feedbacks.filter(feedback=1).count() # returns count of the feedback
negative_feedbacks = feedbacks.filter(feedback=-1).count() # returns count of the feedback
neutral_feedbacks = feedbacks.filter(feedback=0).count() # returns count of the feedback
print("Ratio Positive:Negative:Neutral = {}:{}:{}".format(positive_feedbacks/negative_feedbacks, 1, neutral_feedbacks/negative_feedbacks)
How should I get the recipient of the feedback credentials, should it be passed by URL, how to link recipient to FeedbackModel in class-based-views?
You need to make the user login to the system. For example you can follow this tutorial but there are other good examples out there. Once you login into the system, the user is available via request.user. And your current implement reflects this as well.
How to feedback_score to be calculated each time for every User?
You can get the latest feedback by:
user = User.objects.get(id=1) # or user = request.user
latest_feedback = user.feedback_left_by.last()

Django - Simplify Proxy Model to single class

I have User table in my DB, they can be active or inactive. If I only want to query on active user, I define a Proxy Model like following.
class User(models.Model):
name = models.CharField(max_length=50)
location = models.CharField(max_length=50)
active = models.BooleanField()
class UserActive(models.Manager):
def get_queryset(self):
return super(UserActive, self).get_queryset().filter(active=True)
class ActiveUser(User):
objects = UserActive()
class Meta:
proxy = True
Then by working with ActiveUser, I can do my calculation/statistic with only active user.
The problem is, I need to define both UserActive and ActiveUser class, it seems awkward to me. Because with each main class (in this case is User), we need to define two other classes. Imaging we have several other model need to implement Proxy, the code would look messy. May I know if we can have more elegant way ?
Thanks
Alex
I would really avoid overwriting the .objects manager, and use this as some sort of implicit filtering. The Zen of Python is explicit is better than implicit, by using ActiveUser, you basically implement a filtering manager, but propose it like the entire set.
Perhaps a more elegant solution is to define multiple managers. So we can construct a filtering manager decorator:
def filter_manager(**kwargs):
def decorator(klass):
def get_queryset(self):
return super(klass, self).get_queryset().filter(**kwargs)
klass.get_queryset = get_queryset
return klass
return decorator
This decorator will however throw away a get_queryset that is defined on the manager itself, so you can not perform an extra patch with this.
Now we can define some managers in a rather elegant way:
#filter_manager(active=True)
class ActiveManager(models.Manager):
pass
#filter_manager(active=False)
class InactiveManager(models.Manager):
pass
Finally we can add these managers to the User model, and use explicit names:
class User(models.Model):
name = models.CharField(max_length=50)
location = models.CharField(max_length=50)
active = models.BooleanField()
objects = models.Manager()
active_users = ActiveManager()
inactive_users = InactiveManager()
So now we can use User.active_users to query for the active users. We thus have no proxy models, and can query with User.active_users.count() for example (well we can perform all operations like with .objects but then for .active_users.
I created a new Django project, with only User model. My models.py look like this
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
def filter_manager(**kwargs):
def decorator(klass):
def get_queryset(self):
return super(klass, self).get_queryset().filter(**kwargs)
klass.get_queryset = get_queryset
return klass
return decorator
#filter_manager(active=True)
class ActiveManager(models.Manager):
pass
#filter_manager(active=False)
class InactiveManager(models.Manager):
pass
class User(models.Model):
name = models.CharField(max_length=50)
location = models.CharField(max_length=50)
active = models.BooleanField()
active_user = ActiveManager()
When I tried User.objects.all().
Error: type object 'User' has no attribute 'objects'

Django JSONField to have particular keys

My class looks like this:
class Foo(models.Model):
known_by = JSONField()
My data looks like this
{ "known_by" : [
{'by':'name1', 'value':['def']},
{'by':'name2', 'value':['bar']}
]
}
Is there any way for me to enforce that the Jsonfield needs to follow the format of by,value[] dict. I know how to do this using serialiazers
Any other cleaner way to enforce this(in the model definition itself)? Thanks
You can add a validator to the model field, like this:
class Foo(models.Model):
known_by = ArrayField(JSONField(max_length=100), size=4, validators=[a_json_array_validator])
And the validator is:
def a_json_array_validator(value):
if any([not is_json_valid(entry) for entry in value]):
raise ValidationError(
_('%(value) is not a valid json'),
params={'value': value},
)
(The actual json validation is up to you) ;-)
Note that validators receive python objects so its actually a dict.
You could implement it this way:
from django.db import models
class Bar(models.Model):
by = models.CharField()
value = models.ArrayField()
class Foo(models.Model):
known_by = models.ForeignKey(Bar, on_delete=models.CASCADE)
Why not just override the save method to do the enforcement?
class Foo(models.Model):
known_by = JSONField()
def save(self, *args, **kwargs):
# begin validation code
# end validation code
if valid:
super(Model, self).save(*args, **kwargs)
else:
# something else, maybe http error code?

Django Haystack: filter query based on multiple items in a list.

I have Event model instances that can belong to one or more Organization model instances. I've implemented haystack 2.0.0 to index all of my Events. Here is an example search index.
class EventIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
organization_slug = indexes.CharField(model_attr='organization__slug',
weight=5.0)
organization_name = indexes.CharField(model_attr='organization__name',
weight=5.0)
name = indexes.CharField(model_attr='name', weight=10.0)
....
def get_model(self):
return Event
def index_queryset(self):
return Event.objects.filter()
My question is how do I construct a SearchQuerySet query that filters Events based on one or several Organizations. For example, I want find all Events that belong to "orgnization1" and "organization3" (where the list of organizations can be any length long)
As a Django query it might look something like this:
Event.objects.filter(organization__in=[orgnization1, organization3]).filter(...)
How do I translate this to a haystack query? This is my attempt, but I don't really know what I'm doing...
organization_list = [organization1.slug, organization2.slug]
SearchQuerySet().filter_or(organization__contains=organization_list)
Here is an example of how my models look:
class Event(models.Model):
name = models.CharField(max_length=64)
organization = models.ForeignKey('mymodule.Organization')
...
class Organization(models.Model):
slug = models.SlugField(max_length=64)
name = models.CharField(max_length=64)
...
Any help is much appreciated.
I think I've found a solution. Just sharing it. Apparently, Haystack has an object called a SQ(), which functions similar to Django's Q() object. I found somewhere that you can invoke the add() method on Django's Q object instances to add more query parameters. Seems to work the same way with SQ.
from haystack.forms import SearchForm
from haystack.query import SQ, SearchQuerySet
from haystack.views import SearchView
class CustomSerchView(SearchView):
def __call__(self, request):
self.request = request
########### Custom stuff
user = request.user
organization_list = [organization1.slug, organization2.slug, ....]
sq = SQ()
for slug in organization_list:
sq.add(SQ(organization_slug=slug), SQ.OR)
sqs = SearchQuerySet().filter(sq)
##########
self.form = self.build_form(form_kwargs={'searchqueryset':sqs})
self.query = self.get_query()
self.results = self.get_results()
return self.create_response()