I have this error, how can I fix this?
get() returned more than one Event -- it returned 2!
Can you guys help me understand what that means and maybe tell me in advance how to avoid this error in future?
MODEL
class Event (models.Model):
name = models.CharField(max_length=100)
date = models.DateField(default='')
dicript = models.CharField(max_length=50, default='Описание отсутствует')
category = models.ForeignKey(Category,on_delete=models.CASCADE)
adress = models.TextField(max_length=300)
user = models.ForeignKey(User,related_name="creator",null=True)
subs = models.ManyToManyField(User, related_name='subs',blank=True)
#classmethod
def make_sub(cls, this_user, sub_event):
event, created = cls.objects.get_or_create(
user=this_user
)
sub_event.subs.add(this_user)
VIEWS
def cards_detail (request,pk=None):
# if pk:
event_detail = Event.objects.get(pk=pk)
subs = event_detail.subs.count()
# else:
# return CardsView()
args = {'event_detail':event_detail,'subs':subs}
return render(request,'events/cards_detail.html',args)
class CardsView (TemplateView):`
template_name = 'events/cards.html'
def get (self,request):
events = Event.objects.all()
return render(request,self.template_name,{'events':events })
def subs_to_event (request,pk=None):
event = Event.objects.filter(pk=pk)
Event.make_sub(request.user,event)
return redirect('events:cards')
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
try:
instance = Instance.objects.get(name=name)
except (ObjectDoesNotExist, MultipleObjectsReturned):
pass
get() raises MultipleObjectsReturned if more than one object was found,more info here.
the error is cause by event_detail = Event.objects.get(pk=pk), check your event pk is unique.
Basically, the cls object is getting more than one value on the get part of the 'get_or_create()'. get() returns only a single object whereas filter returns a dict(ish).
Put it in a try/except instead. So you'll have:
try:
event, created = cls.objects.get_or_create(
user=this_user
)
except cls.MultipleObjectsReturned:
event = cls.objects.filter(user=this_user).order_by('id').first()
This way if multiple objects are found, it handles the exception and changes the query to a filter to receive the multiple object queryset. No need to catch the Object.DoesNotExist as the create part creates a new object if no record is found.
I also face the same error:
get() returned more than one -- it returned 4!
The error was that I forgot to make a migration for the newly added fields in the model.
Related
I have a model (Event) that has a ForeignKey to the User model (the owner of the Event).
This User can invite other Users, using the following ManyToManyField:
invites = models.ManyToManyField(
User, related_name="invited_users",
verbose_name=_("Invited Users"), blank=True
)
This invite field generates a simple table, containing the ID, event_id and user_id.
In case the Event owner deletes his profile, I don't want the Event to be deleted, but instead to pass the ownership to the first user that was invited.
So I came up with this function:
def get_new_owner():
try:
invited_users = Event.objects.get(id=id).invites.order_by("-id").filter(is_active=True)
if invited_users.exists():
return invited_users.first()
else:
Event.objects.get(id=id).delete()
except ObjectDoesNotExist:
pass
This finds the Event instance, and returns the active invited users ordered by the Invite table ID, so I can get the first item of this queryset, which corresponds to the first user invited.
In order to run the function when a User gets deleted, I used on_delete=models.SET:
owner = models.ForeignKey(User, related_name='evemt_owner', verbose_name=_("Owner"), on_delete=models.SET(get_new_owner()))
Then I ran into some problems:
It can't access the ID of the field I'm passing
I could'n find a way to use it as a classmethod or something, so I had to put the function above the model. Obviously this meant that it could no longer access the class below it, so I tried to pass the Event model as a parameter of the function, but could not make it work.
Any ideas?
First we can define a strategy for the Owner field that will call the function with the object that has been updated. We can define such deletion, for example in the <i.app_name/deletion.py file:
# app_name/deletion.py
def SET_WITH(value):
if callable(value):
def set_with_delete(collector, field, sub_objs, using):
for obj in sub_objs:
collector.add_field_update(field, value(obj), [obj])
else:
def set_with_delete(collector, field, sub_objs, using):
collector.add_field_update(field, value, sub_objs)
set_with_delete.deconstruct = lambda: ('app_name.SET_WITH', (value,), {})
return set_with_delete
You should pass a callable to SET, not call the function, so you implement this as:
from django.conf import settings
from django.db.models import Q
from app_name.deletion import SET_WITH
def get_new_owner(event):
invited_users = event.invites.order_by(
'eventinvites__id'
).filter(~Q(pk=event.owner_id), is_active=True).first()
if invited_users is not None:
return invited_users
else:
event.delete()
class Event(models.Model):
# …
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name='owned_events',
verbose_name=_('Owner'),
on_delete=models.SET_WITH(get_new_owner)
)
Here we thus will look at the invites to find a user to transfer the object to. Perhaps you need to exclude the current .owner of the event in your get_new_owner from the collection of .inivites.
We can, as #AbdulAzizBarkat says, better work with a CASCADE than explicitly delete the Event object , since that will avoid infinite recursion where an User delete triggers an Event delete that might trigger a User delete: at the moment this is not possible, but later if extra logic is implemented one might end up in such case. In that case we can work with:
from django.db.models import CASCADE
def SET_WITH(value):
if callable(value):
def set_with_delete(collector, field, sub_objs, using):
for obj in sub_objs:
val = value(obj)
if val is None:
CASCADE(collector, field, [obj], using)
else:
collector.add_field_update(field, val, [obj])
else:
def set_with_delete(collector, field, sub_objs, using):
collector.add_field_update(field, value, sub_objs)
set_with_delete.deconstruct = lambda: ('app_name.SET_WITH', (value,), {})
return set_with_delete
and rewrite the get_new_owner to:
def get_new_owner(event):
invited_users = event.invites.order_by(
'eventinvites__id'
).filter(~Q(pk=event.owner_id), is_active=True).first()
if invited_users is not None:
return invited_users
else: # strictly speaking not necessary, but explicit over implicit
return None
I have a Django model which needs to have more than 1 images and more than 1 files (numbers may vary as per requirement), for which I adjusted my Admin Panel accordingly like this
models.py
class MasterIndividualMembers(models.Model):
individualmemberId = models.CharField(primary_key=True, max_length=100, default=1)
...
...
def __str__(self):
return self.firstname + " " + self.lastname
class IndividualMemberPhotos(models.Model):
individualmemberId = models.ForeignKey(MasterIndividualMembers, default=None,on_delete=models.CASCADE)
image = models.ImageField(upload_to="individualmemberphotos/")
class IndividualMemberCatalogue(models.Model):
individualmemberId = models.ForeignKey(MasterIndividualMembers, default=None,on_delete=models.CASCADE)
files = models.FileField(upload_to="individualmembercatalogue/")
admin.py
class IndividualMemberPhotosAdmin(admin.StackedInline):
model = IndividualMemberPhotos
class IndividualMemberCatalogueAdmin(admin.StackedInline):
model = IndividualMemberCatalogue
#admin.register(MasterIndividualMembers)
class MasterIndividualMembersAdmin(admin.ModelAdmin):
inlines = [IndividualMemberPhotosAdmin,IndividualMemberCatalogueAdmin]
class Meta:
model = MasterIndividualMembers
For the views I simply make a function to provide details of all the Images, Document and that User
views.py
#csrf_exempt
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def get_individualmember(request):
if request.method == 'POST':
try:
individualmemberId = request.POST.get('individualmemberId')
result = {}
result['individualMemberDetails'] = json.loads(serializers.serialize('json', [MasterIndividualMembers.objects.get(individualmemberId=individualmemberId)]))
result['individualPhotoDetails'] = json.loads(serializers.serialize('json', IndividualMemberPhotos.objects.filter(individualmemberId__individualmemberId = individualmemberId)))
result['individualCatalogueDetails'] = json.loads(serializers.serialize('json', IndividualMemberCatalogue.objects.filter(individualmemberId__individualmemberId = individualmemberId)))
except Exception as e:
return HttpResponseServerError(e)
Problem: While fetching the details for any individual member, it throws an error get() returned more than one IndividualMemberPhotos -- it returned 2!, which is expected to have more than 1 objects.
How can I make the Restframework to provide me details of all image object together.
Instead of using get() which strictly returns a single element, use filter() which returns 0 or more elements.
As documented in https://docs.djangoproject.com/en/3.2/topics/db/queries/#retrieving-a-single-object-with-get
filter() will always give you a QuerySet, even if only a single object
matches the query - in this case, it will be a QuerySet containing a
single element.
If you know there is only one object that matches your query, you can
use the get() method on a Manager which returns the object directly:
The behavior you are experiencing is actually documented here https://docs.djangoproject.com/en/3.2/ref/models/querysets/#django.db.models.query.QuerySet.get
If get() finds more than one object, it raises a
Model.MultipleObjectsReturned exception:
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'm trying to work with severals objects to achieve an action.
My models.py
class LogBook(models.Model):
name = models.CharField()
class LogMessage(models.Model):
logbook = models.ForeignKey(LogBook, on_delete=models.CASCADE)
class LogDone(models.Model):
logmessage = models.ForeignKey(LogMessage)
done_status = models.BooleanField(default=False)
My view :
def logmessage_done(request, logmessage_id, log_id, token ):
log = get_object_or_404(LogBook, pk=log_id)
logmessages = LogMessage.objects.filter(logbook=log)
logdone = LogDone.objects.get_or_create(logmessage=logmessages)
logdone.done_status = True
logdone.update()
My url :
"done/<int:logmessage_id>/<int:log_id>/<str:token>"
What I want to achieve :
I want to change the status of the logdone object which is link to the logmessage object but I am not sure I have access object correctly.
What error I have :
The QuerySet value for an exact lookup must be limited to one result using slicing.
Change your view like this:
def logmessage_done(request, logmessage_id, log_id, token ):
log = get_object_or_404(LogBook, pk=log_id)
logmessages = LogMessage.objects.filter(logbook=log)
for log_message in logmessages:
LogDone.objects.update_or_create(logmessage=log_message,defaults={"done_status": True})
Here , log returns a single object with id . logmessages returns a queryset with logbook = the log returned in first query. Have to use update_or_create method
I've an Order model and others models which related with it. An user can delete any of this items and I must perform a check if the order is empty after deletion and set as active False in case true. Some basic code to ilustrate it
class Order(models.Model):
paid = models.BooleanField(default=False)
active = models.BooleanField(default=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
def empty_order():
"""
I must implement it
"""
class HomeOrder(models.Model):
...
order = models.OneToOneField(Order, related_name='primary_home')
class TourOrder(models.Model):
...
order = models.ForeignKey(Order, related_name='tours')
I have a post_delete signals that are connected with every of this Models related to Order:
post_delete.connect(delete_order_if_empty, sender=HomeOrder)
post_delete.connect(delete_order_if_empty, sender=TourOrder)
def delete_order_if_empty(sender, instance, **kwargs):
if instance.order.empty_order():
instance.order.active = False
instance.order.save()
An Order can have one Home, so if the Home exists I can do order.primary_home, if Home does not exist it will raise an AttributeError because it is an OneToOne relationship.
An Order can have many Tours, so in the empty_order method I thought to do some checks as following.
def empty_order():
home = hasattr(self, 'primary_home') # Avoid AttributeError exception
tours = self.tours.exists()
this_order_has_something = primary_home or tours
return not this_order_has_something
Now, when I delete an HomeOrder the signal is raised but the empty_method never realized that this HomeOrder does not exists any more. Example:
>>>o=Order.objects.create(...)
>>>o.primary_home # raise AttributeError
>>>h=HomeOrder.objetcs.create(order=o, ...)
>>>o.primary_home # <HomeOrder: home-xx>
>>>h.delete()
>>>o.primary_home # still <HomeOrder: home-xx> Why?
>>>o.refresh_from_db()
>>>o.primary_home # again <HomeOrder: home-xx>