I'm trying to achive an annotated query and here's my code.
# models.py
STATUS_CHOICES = (
("submitted", "제출됨"),
("in-review", "검토중"),
("rejected", "반려됨"),
("approved", "승인됨"),
)
class ClassReview(models.Model):
class = models.ForeignKey("class.Class", on_delete=models.PROTECT)
status = models.CharField(max_length=10, choices=STATUS_CODE, default="submitted")
class ClassReviewVote(models.Model):
target = models.ForeignKey("class.ClassReview", on_delete=models.PROTECT)
vote = models.BooleanField(null=True)
# selectors.py
def get_classreview_set(class_id):
review_set = ClassReview.objects.filter(class_id=class_id, status="approved")
review_Set = review_set.annotate(
vote_up_count=Subquery(
ClassReviewVote.objects.filter(target_id=OuterRef("pk"), vote=True)
.values("target")
.annotate(count=Count("target"))
.values("count"),
output_field=IntegerField(),
)
)
return review_set
# serializers.py
def classreview_serializer(classreview):
data = dict()
...
data["vote_up_count"] = classreview.vote_up_count
...
return data
# views.py
class ClassDetailView(TemplateView):
...
def get(self, request, *args, **kwargs):
class_id = request.kwargs.get("class_id")
review_set = get_classreview_set(class_id)
context = {
review_list = classreview_serializer(review_set)
}
...
...
I want to annotate vote up count of each review.
But it keeps raising Error "more than one row returned by a subquery used as an expression".
When logged in user and someone else voted up. What is happening?
I found why error occurred. There is ordering in model's META class. So it didn't work well.
I guess error occurred when GROUP_BY happens, cuz there is ordering already.
Here's code:
# selectors.py
def get_classreview_set(class_id):
review_set = ClassReview.objects.filter(class_id=class_id, status="approved")
review_Set = review_set.annotate(
vote_up_count=Subquery(
ClassReviewVote.objects.filter(target_id=OuterRef("pk"), vote=True)
.order_by("target") # <------ NEW LINE
.values("target")
.annotate(count=Count("target"))
.values("count"),
output_field=IntegerField(),
)
)
return review_set
It works!!
I don't know what's the problem exactly until now. But I fixed it.
If there is someone knows what's happening exactly, let me know plz.
Related
I'm trying to make a chatting room. and here is my code.
def make_chatroom(request, user_one, user_two):
user_one = user_models.User.objects.get_or_none(pk=user_one)
user_two = user_models.User.objects.get_or_none(pk=user_two)
if user_one is not None and user_two is not None:
try:
conversation = models.Conversation.objects.filter(
participants=user_one
).filter(participants=user_two)
print(conversation)
except models.Conversation.DoesNotExist:
conversation = models.Conversation.objects.create()
conversation.participants.add(user_one, user_two)
return redirect(reverse("conversations:detail", kwargs={"pk": conversation.pk}))
but
'QuerySet' object has no attribute 'pk'
error occur.
user_one is exist, and user_two also exist. and print clause print
<QuerySet [<Conversation: master, test>]>
How can I fix it?
my models.py:
class Conversation(core_models.TimeStampedModel):
participants = models.ManyToManyField(
"users.User", related_name="converstation", blank=True
)
def __str__(self):
usernames = []
for user in self.participants.all():
usernames.append(user.username)
return ", ".join(usernames)
def count_messages(self):
return self.messages.count()
count_messages.short_description = "메세지 수"
def count_participants(self):
return self.participants.count()
count_participants.short_description = "참가자 수"
class Message(core_models.TimeStampedModel):
message = models.TextField()
user = models.ForeignKey(
"users.User", related_name="messages", on_delete=models.CASCADE
)
conversation = models.ForeignKey(
"Conversation", related_name="messages", on_delete=models.CASCADE
)
def __str__(self):
return f"{self.user} says: {self.message}"
Your problem lies in that conversation ends up being one of two types depending on whether an error is thrown or not.
No error:
conversation = models.Conversation.objects.filter(
participants=user_one
).filter(participants=user_two)
In this case conversation is a queryset, that is all objects that match the filters you've specified.
Error:
conversation = models.Conversation.objects.create()
In this case conversation is a Conversation instance, so it will have the pk attribute.
What I think you're missing here is a call to get at the end of your query. As it stands the query will never throw the error models.Conversation.DoesNotExist
I hope that clears it up!
P.S. It's actually possible for two errors to be thrown when calling get, you can also get a MultipleObjectsReturned error. If you are absolutely positive that will never happen you don't need to handle it but I thought I should let you know
I am working with Django Rest Framework Filter to access my data. I wrote a custom viewset for one of my models to handle logical 'or' operation on some specifics fields for request with url.
Here is my ViewSet Class (the printing are just for my debug)
class CustomViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
queryset = appart.MyModel.objects.order_by('pk')
serializer_class = MyModelSerializer
filter_class = MyModelFilter
def get_queryset(self):
# getting param
query = dict(self.request.query_params)
print(query)
ct_query = query['caracteristique_text__icontains'][0].split('|')
cl_query = query['caracteristique__libelle__in'][0].split(',')
# writting request syntax
ct = 'Q(caracteristique__libelle__in=' + str(cl_query) + ')&('
print(ct)
for value in ct_query:
ct += "Q(caracteristique_text__icontains='" + value + "')|"
ct = ct[0:len(ct) - 1]
ct += ')'
print(ct)
filtre_text = "global filtre; filtre = " + ct
# creating the request
exec(filtre_text)
#running the request
self.queryset = self.queryset.filter(filtre)
print(self.queryset)
return self.queryset # doesn't return what I see when running print(self.queryset)
And MyModelFilter Class :
class MyModelFilter(ModelFilterSet):
class Meta:
model = appart.MyModel
fields = ('id', 'libelle', 'locataire_appart', 'bien_appart',
'adresse', 'loyer_appart', 'caracteristique', 'caracteristique_text',
'date_creation', 'derniere_maj')
this code is working well and do what I want it to do. He takes the url parameters, create dynamically the request syntax and return the data i'm looking for.
The only problem is that the return at the end doesn't give the same result that what I printed in my code. He still trying to execute a GET request based on the given url and return nothing when the syntax is not following the django_filter rules.
Can anyone help me to prevent this behavior of my viewsets ?
You need to specify some filters with Django Filters. I think you just need BaseInFilter: https://django-filter.readthedocs.io/en/master/ref/filters.html#baseinfilter
class MyModelFilter(ModelFilterSet):
caracteristique_text = BaseInFilter(field_name='caracteristique_text', lookup_exp='icontains')
libelle = BaseInFilter(field_name='libelle', lookup_exp='in')
class Meta:
model = appart.MyModel
fields = ('id', 'libelle', 'locataire_appart', 'bien_appart',
'adresse', 'loyer_appart', 'caracteristique', 'caracteristique_text',
'date_creation', 'derniere_maj')
Ok so I finally solved my problem, there were no mistake in the code. This was due to the filter_class = MyModelFilter. I just commented on this line and everything started to work. I honestly don't know why but i would appreciate a comment to this answer if anyone know the reason.
I also took a look on the exec that #schillingt was talking about and here is the result for those trying to do the same thing or having the same problem.
class CustomViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
queryset = models.MyModel.objects.order_by('pk')
serializer_class = MyModelSerializer
filter_class = MyModelFilter
def get_queryset(self):
query = dict(self.request.query_params)
ct = self.request.query_params.get('caracteristique_text__icontains', None)
cl = self.request.query_params.get('caracteristique__libelle__in', None)
if ct is not None and cl is not None:
self.filter_class = None
ct_query = query['caracteristique_text__icontains'][0].split(',')
cl_query = query['caracteristique__libelle__in'][0].split(',')
full_query = Q(caracteristique__libelle__in=cl_query)
ct = Q()
for value in ct_query:
ct_part = Q(caracteristique_text__icontains=value)
ct |= ct_part
full_query &= ct
self.queryset = self.queryset.filter(full_query)
return self.queryset
return models.MyModel.objects.order_by('pk')
This post can also be helpful Filter with or condition
I'm building a simple recipe storage application that uses the Graphene package for GraphQL. I've been able to use Django Forms so far very easily in my mutations, however one of my models fields is really an Enum and I'd like to expose it in Graphene/GraphQL as such.
My enum:
class Unit(Enum):
# Volume
TEASPOON = "teaspoon"
TABLESPOON = "tablespoon"
FLUID_OUNCE = "fl oz"
CUP = "cup"
US_PINT = "us pint"
IMPERIAL_PINT = "imperial pint"
US_QUART = "us quart"
IMPERIAL_QUART = "imperial quart"
US_GALLON = "us gallon"
IMPERIAL_GALLON = "imperial gallon"
MILLILITER = "milliliter"
LITER = "liter"
# Mass and Weight
POUND = "pound"
OUNCE = "ounce"
MILLIGRAM = "milligram"
GRAM = "gram"
KILOGRAM = "kilogram"
My Model:
class RecipeIngredient(TimeStampedModel):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name='ingredients')
direction = models.ForeignKey(RecipeDirection, on_delete=models.CASCADE, null=True, related_name='ingredients')
quantity = models.DecimalField(decimal_places=2, max_digits=10)
unit = models.TextField(choices=Unit.as_tuple_list())
My form:
class RecipeIngredientForm(forms.ModelForm):
class Meta:
model = RecipeIngredient
fields = (
'recipe',
'direction',
'quantity',
'unit',
)
My Mutation:
class CreateRecipeIngredientMutation(DjangoModelFormMutation):
class Meta:
form_class = RecipeIngredientForm
exclude_fields = ('id',)
I've created this graphene enum UnitEnum = Enum.from_enum(Unit) however I haven't been able to get graphene to pick it up. I've tried adding it to the CreateRecipeIngredientMutation as a regular field like unit = UnitEnum() as well as an Input class on that mutation. So far, the closest I've gotten is this Github issue from awhile ago. After playing around with the class in an iPython shell, I think I could just do CreateRecipeIngredientMutation.Input.unit.type.of_type = UnitEnum() but this feels awful.
I came up with a solution that works but is not pretty. I used the https://github.com/hzdg/django-enumfields package to help with this.
I created my own form field:
class EnumChoiceField(enumfields.forms.EnumChoiceField):
def __init__(self, enum, *, coerce=lambda val: val, empty_value='', **kwargs):
if isinstance(enum, six.string_types):
self.enum = import_string(enum)
else:
self.enum = enum
super().__init__(coerce=coerce, empty_value=empty_value, **kwargs)
And used it in my Django form. Then in my custom AppConfig I did this:
class CoreAppConfig(AppConfig):
name = 'myapp.core'
def ready(self):
registry = get_global_registry()
#convert_form_field.register(EnumChoiceField)
def convert_form_field_to_enum(field: EnumChoiceField):
converted = registry.get_converted_field(field.enum)
if converted is None:
raise ImproperlyConfigured("Enum %r is not registered." % field.enum)
return converted(description=field.help_text, required=field.required)
And finally in my schema:
UnitEnum = Enum.from_enum(Unit)
get_global_registry().register_converted_field(Unit, UnitEnum)
I really don't like this, but couldn't think of a better way to handle this. I came across this idea when searching down another graphene django issue here https://github.com/graphql-python/graphene-django/issues/481#issuecomment-412227036.
I feel like there has to be a better way to do this.
I've Patient, Doctor, Story Model. Each Story have a patient_id and a doctor_id. I want to retrieve a list of doctors the patient have visited ever.
class Patient(Person):
def visits(self):
doctor_visits = []
for v in self.stories.values('doctor').annotate(visits=Count('doctor')):
# replace the doctor id with doctor object
v['doctor'] = Doctor.objects.get(id=v['doctor'])
doctor_visits.append(v)
return doctor_visits
Here is my tastypie Resource
class PatientResource(ModelResource):
stories = fields.ToManyField('patients.api.StoryResource', 'stories', null=True)
visits = fields.ListField(attribute='visits', readonly=True)
class Meta:
queryset = Patient.objects.all()
excludes = ['id', 'login', 'password']
with the above tastypie results the following
{
address:"ADDRESS",
dob:"1985-12-04",
email:"EMAIL",
name:"Nogen",
resource_uri:"/patients/api/v1/patient/9/",
sex:"M",
stories:[
"/patients/api/v1/story/1/",
"/patients/api/v1/story/2/",
"/patients/api/v1/story/4/"
],
visits:[
{
doctor:"Dr. X",
visits:2
},
{
doctor:"Dr. Y",
visits:1
}
]
}
See Its caling the __unicode__ method of Doctor rather I expected this to be a link /patients/api/v1/doctor/<doctor_id>/ Do I need to construct the path manually or There is some other way around ?
I've tried using dehydrate possibly incorrectly
class PatientResource(ModelResource):
stories = fields.ToManyField('patients.api.StoryResource', 'stories', null=True)
visits = fields.ListField(attribute='visits', readonly=True)
class Meta:
queryset = Patient.objects.all()
excludes = ['id', 'login', 'password']
def dehydrate_visits(self, bundle):
for visit in bundle.data['visits']:
visit['doctor'] = DoctorResource(visit['doctor'])
return bundle
Which Results in maximum recursion depth exceeded while calling a Python object Exception
Not sure why you get maximum recursion depth but your method is wrong.
class PatientResource(ModelResource):
[...]
def dehydrate_visits(self, bundle):
# Make sure `bundle.data['visits'][0]['doctor'] isn't string.
# If it's already dehydrated string: try use `bundle.obj.visits` instead.
for visit in bundle.data['visits']:
visit['doctor'] = DoctorResource.get_resource_uri(visit['doctor'])
return bundle
I didn't test that. So fill free to comment if its incorrect.
I am learning Django, and want to retrieve all objects that DON'T have a relationship to the current object I am looking at.
The idea is a simple Twitter copycat.
I am trying to figure out how to implement get_non_followers.
from django.db import models
RELATIONSHIP_FOLLOWING = 1
RELATIONSHIP_BLOCKED = 2
RELATIONSHIP_STATUSES = (
(RELATIONSHIP_FOLLOWING, 'Following'),
(RELATIONSHIP_BLOCKED, 'Blocked'),
)
class UserProfile(models.Model):
name = models.CharField(max_length=200)
website = models.CharField(max_length=200)
email = models.EmailField()
relationships = models.ManyToManyField('self', through='Relationship',
symmetrical=False,
related_name='related_to')
def __unicode__ (self):
return self.name
def add_relationship(self, person, status):
relationship, created = Relationship.objects.get_or_create(
from_person=self,
to_person=person,
status=status)
return relationship
def remove_relationship(self, person, status):
Relationship.objects.filter(
from_person=self,
to_person=person,
status=status).delete()
return
def get_relationships(self, status):
return self.relationships.filter(
to_people__status=status,
to_people__from_person=self)
def get_related_to(self, status):
return self.related_to.filter(
from_people__status=status,
from_people__to_person=self)
def get_following(self):
return self.get_relationships(RELATIONSHIP_FOLLOWING)
def get_followers(self):
return self.get_related_to(RELATIONSHIP_FOLLOWING)
def get_non_followers(self):
# How to do this?
return
class Relationship(models.Model):
from_person = models.ForeignKey(UserProfile, related_name='from_people')
to_person = models.ForeignKey(UserProfile, related_name='to_people')
status = models.IntegerField(choices=RELATIONSHIP_STATUSES)
This isn't particularly glamorous, but it gives correct results (just tested):
def get_non_followers(self):
UserProfile.objects.exclude(to_people=self,
to_people__status=RELATIONSHIP_FOLLOWING).exclude(id=self.id)
In short, use exclude() to filter out all UserProfiles following the current user, which will leave the user themselves (who probably shouldn't be included) and all users not following them.
i'v been searching for a method or some way to do that for like an hour, but i found nothing.
but there is a way to do that.
you can simply use a for loop to iterate through all objects and just remove all objects that they have a special attribute value.
there is a sample code here:
all_objects = className.objects.all()
for obj in all_objects:
if obj.some_attribute == "some_value":
all_objects.remove(obj)
Solution to the implementation of get_non_followers:
def get_non_following(self):
return UserProfile.objects.exclude(to_person__from_person=self, to_person__status=RELATIONSHIP_FOLLOWING).exclude(id=self.id)
This answer was posted as an edit to the question Finding objects without relationship in django by the OP Avi Meir under CC BY-SA 3.0.
current_userprofile = current_user.get_profile()
rest_of_users = Set(UserProfile.objects.filter(user != current_userprofile))
follow_relationships = current_userprofile.relationships.filter(from_person=current_user)
followers = Set();
for follow in follow_relationships:
followers.add(follow.to_person)
non_followeres = rest_of_users.difference(followers)
Here non_followers is the list of userprofiles you desire. current_user is the user whose non_followers you are trying to find.
I haven't tested this out, but it think it should do what you want.
def get_non_followers(self):
return self.related_to.exclude(
from_people__to_person=self)