django - get() returned more than one topic
Asked Answered
H

9

101

When I tried to relate an attribute with another one which has an M to M relation I received this error:

get() returned more than one topic -- it returned 2!

Can you guys tell me what that means and maybe tell me in advance how to avoid this error ?

models

class LearningObjective(models.Model):
    learning_objective=models.TextField()

class Topic(models.Model):
    learning_objective_topic=models.ManyToManyField(LearningObjective)
    topic=models.TextField()

output of LearningObjective.objects.all()

[<LearningObjective: lO1>, <LearningObjective: lO2>, <LearningObjective: lO3>]

output of Topic.objects.all()

[<Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>, <Topic: Topic object>]

views

 def create_themen(request):
     new_topic=Topic(topic=request.POST['topic'])
     new_topic.save()
     return render(request, 'topic.html', {'topic': topic.objects.all()})

 def create_learning_objective(request):
     new_learning_objective=LearningObjective(learning_objective=request.POST['learning_objective'])
     new_learning_objective.save()
     new_learning_objective_topic=Topic.objects.get(topic=request.POST['topic'])
     new_learning_objective_topic.new_learning_objective_topic.add(new_learning_objective)
     return render( request, 'learning_objective.html', {
                    'topic': Topic.objects.all(),
                    'todo': TodoList.objects.all(),
                    'learning_objective': LearningObjective.objects.all()
                  })
Hoicks answered 27/2, 2014 at 9:6 Comment(2)
Provide more code please.Nitza
that's not very useful, post the part of the code where you're using get. In any case, get is limited to 1 result, if you're making a query that matches more than one element you should use filterSchleswig
P
208

get() returned more than one topic -- it returned 2!

The above error indicatess that you have more than one record in the DB related to the specific parameter you passed while querying using get() such as

Model.objects.get(field_name=some_param)

To avoid this kind of error in the future, you always need to do query as per your schema design. In your case you designed a table with a many-to-many relationship so obviously there will be multiple records for that field and that is the reason you are getting the above error.

So instead of using get() you should use filter() which will return multiple records. Such as

Model.objects.filter(field_name=some_param)

Please read about how to make queries in django here.

Piscatorial answered 27/2, 2014 at 9:30 Comment(0)
P
23

get() returns a single object. If there is no existing object to return, you will receive <class>.DoesNotExist. If your query returns more than one object, then you will get MultipleObjectsReturned. You can check here for more details about get() queries.

Similarly, Django will complain if more than one item matches the get() query. In this case, it will raise MultipleObjectsReturned, which again is an attribute of the model class itself.

M2M will return any number of query that it is related to. In this case you can receive zero, one or more items with your query.

In your models you can us following:

for _topic in topic.objects.all():
    _topic.learningobjective_set.all()

you can use _set to execute a select query on M2M. In above case, you will filter all learningObjectives related to each topic

Parsifal answered 27/2, 2014 at 9:23 Comment(0)
A
6

Get is supposed to return, one and exactly one record, to fix this use filter(), and then take first element of the queryset returned to get the object you were expecting from get, also it would be useful to check if atleast one record is returned before taking out the first element to avoid IndexError

Apt answered 27/2, 2014 at 9:22 Comment(1)
Yes topicresults = topic.objects.filter(topic='Thema') if topicresults: topicresult = topicresults[0]Apt
F
6

I had same problem and solution was obj = ClassName.objects.filter()

Fredericafrederich answered 7/8, 2017 at 17:23 Comment(0)
S
4

In your Topic model you're allowing for more than one element to have the same topic field. You have created two with the same one already.

topic=models.TextField(verbose_name='Thema')

Now when trying to add a new learningObjective you seem to want to add it to only one Topic that matches what you're sending on the form. Since there's more than one with the same topic field get is finding 2, hence the exception.

You can either add the learningObjective to all Topics with that topic field:

for t in topic.objects.filter(topic=request.POST['Thema']):
    t.learningObjectivesTopic.add(neuesLernziel)

or restrict the Topic model to have a unique topic field and keep using get, but that might not be what you want.

Schleswig answered 27/2, 2014 at 9:36 Comment(1)
okay your solution worked in both ways, the unique field of course worked but wasn't what I wanted, you were right at this point :) and the for loop is working like charm tooHoicks
V
0

To add to CrazyGeek's answer, get or get_or_create queries work only when there's one instance of the object in the database, filter is for two or more.

If a query can be for single or multiple instances, it's best to add an ID to the div and use an if statement e.g.

def updateUserCollection(request):
    data = json.loads(request.body)
    card_id = data['card_id']
    action = data['action']

    user = request.user
    card = Cards.objects.get(card_id=card_id)

    if data-action == 'add':
        collection = Collection.objects.get_or_create(user=user, card=card)
        collection.quantity + 1
        collection.save()

    elif data-action == 'remove':
        collection = Cards.objects.filter(user=user, card=card)
        collection.quantity = 0
        collection.update()

Note: .save() becomes .update() for updating multiple objects. Hope this helps someone, gave me a long day's headache.

Vickivickie answered 17/6, 2020 at 13:5 Comment(0)
L
0

Use this in the case when you required one record but you're not having a primary key or unique to identify the object.

XYZ.objects.filter(model_attribute=your_value)[0]

This will return a normal object same as the .get().

Ligamentous answered 14/10, 2021 at 20:11 Comment(0)
J
0

Use .filter then use .latest selection by pk to get one object from filtered ones

tempobj = Flattable()
        try:
             tempobj = Flattable.objects.filter(org_address__icontains=row[5]).latest('pk')
        except Flattable.DoesNotExist:
             tempobj = None
Jobless answered 25/9, 2023 at 11:43 Comment(0)
S
-1

Don't :-

xyz = Blogs.objects.get(user_id=id)

Use:-

xyz = Blogs.objects.all().filter(user_id=id)
Seychelles answered 3/12, 2020 at 18:21 Comment(1)
To improve the quality of this answer you could explain to us why your code is a better alternative and what they have done wrong. Your answer is very brief and could include at least a small sentence on how you think this is a better approach.Korrie

© 2022 - 2024 — McMap. All rights reserved.