0
models.py
class Fish(models.Model):
    sci_name = models.CharField(max_length=50)
    com_name = models.CharField(max_length=50)
    ...

class Info(models.Model):
    fish = models.ManyToManyField(Fish)
    short_description = models.TextField(max_length=200)
    ...

views.py
def available(request):
    in_stock = Info.objects.order_by('id')
    ...
    context = RequestContext(request, {
        'in_stock': in_stock,
    ...

available.html
    {% for fishnumber in in_stock %}
        {{ fishnumber.short_description }}     //this line works
        {{ fishnumber.fish.sci_name }}         //this line doesn't work
        {% for fish in fishnumber.fish.all %}  //this loop works
            {{ fish.sci_name }}
        {% endfor %}

I want to access the sci_name field through the many to many relationship Info has with Fish, without that second for loop. I have searched the documentation and google, but all the answers I can find show how to access the field using a loop through fishnumber.fish.all. This works fine, but I want to specify the exact row in the Fish table and select the element from the sci_name column, without looping through all the rows in the many to many relationship between Info and Fish.

user3325088
  • 65
  • 1
  • 4
  • What do you mean by "not looping"? How do you expect the end result to look like? – yuvi Feb 18 '14 at 20:52
  • I would like to just access the sci_name field directly with something like fishnumber.fish.sci_name , but there is something about the syntax that I am missing. – user3325088 Feb 18 '14 at 20:55
  • I don't know how to specify the specific one of the many to many relationships I want to access, so for now I can only access them one after another with the loop. I would like the single sci_name element to be displayed on the page. Now all I can get are three sci_name elements displayed one after another. – user3325088 Feb 18 '14 at 20:59
  • You're not missing anything about the syntax, you're just being very unclear about what you're trying to do. What's the problem? How would you like it to behave? To what end result your attempting to reach, etc. – yuvi Feb 18 '14 at 21:03
  • Sorry, I am trying to be specific, I just don't know the right words to use. I would like to display on the page one single sci_name element. Now I can only display all three sci_name elements in a row – user3325088 Feb 18 '14 at 21:05
  • How can I access a single sci_name element in a single row from the many to many relationship between Info and Fish. – user3325088 Feb 18 '14 at 21:17
  • i don't know if i understood, but check my answer – Luis Masuelli Feb 18 '14 at 21:18
  • accessing a single element is accessing an element inside a query, when talking about M2M relations. – Luis Masuelli Feb 18 '14 at 21:18

2 Answers2

0

If you want to get a single nth element do this:

all_qs = (a specific Info instance).fish.all() #i suggest using an order criteria here
myobject = all_qs[0] #get the first object returned by the query
myval = myobject.sci_name

catch exceptions here -- index may not exist, even being 0.
if you want to get by another criteria, specify the criteria in the query and get the Xth item you want (perhaps the first).

remember that M2M are "managers" and they execute queries -- they are not arrays.

remember again: you can specify [anyIndex] between brackets, and even [any:slice]. both will be treated as LIMIT offset, amount (with the proper numbers mapping, e.g. [1:6] will become LIMIT 1, 5 (i.e. 6-1)). negative indexes are not supported.

actually, i don't know what criteria do you need:

  1. if you need a specific criteria, preprocess query = infoInstance.fish.filter(criteriaHere)
  2. otherwise, preprocess query = infoInstance.fish.all()

after that, you can:

  1. if you expect that only one object with that criteria exist, and being 0 or 2+ objects is an error, do object=query.get() instead of filter()
  2. if you need a specific object at a specific position, keep the filter() or all(), and index as follows: object=query[positionYouWant]
yuvi
  • 18,155
  • 8
  • 56
  • 93
Luis Masuelli
  • 12,079
  • 10
  • 49
  • 87
  • I am trying to access the element from within the template. I understand how to do this in normal python code, but I cant get it to work inside the template. Do I have to send the template more information than just 'in_stock': in_stock, – user3325088 Feb 18 '14 at 21:26
  • yes, since you cannot index / pass parameters from within a template. you have to preprocess that and pass the result to the template. OR... you should register your own tag or filter doing that. – Luis Masuelli Feb 18 '14 at 21:32
  • Thanks for the input, do you have any suggestions on what/how to send to the template? – user3325088 Feb 18 '14 at 21:38
  • It seems odd that I can have access to all the elements when I use a for loop, but then not if I want to access just a specific one. – user3325088 Feb 18 '14 at 21:47
  • because to access a specific one, you should actually retrieve it. "fish" is not an instance, but a manager. the manager lets u execute queries. run a query with the criteria you want, and then obtain the object at a specific position by indexing. warning: exceptions may appear here. capture them. – Luis Masuelli Feb 18 '14 at 21:49
0

You need to have some way of deciding which one you want. I mean - say there are three fish, how do you distinguish the specific one you want? Each one has its own sci_name. To decide on one of them, you need to provide that functionality.

Or, in other words, just use a model method. Let's say you want to catch the latest fish, and present its sci_name. You'd do it like so

class Info(models.Model):
    ...
    def latest_catch(self):
         return self.fish.latest().sci_name

Then in the template:

{% for fishnumber in in_stock %}
    {{ fishnumber.short_description }}     
    {{ fishnumber.latest_catch }}       
{% endfor %}

Or maybe you want all related sci_names joined by a comma:

class Info(models.Model):
    ...
    def fish_names(self):
        return ', '.join(self.fish.values('sci_name'))

*I don't remember exactly how .values behaves, maybe a direct join wouldn't work. Whatever, you get the picture, right?

If you need to pass some argument to that function (say an id to filter the fish on), you can't do it in the template unless you build your own template tag or something (not such a big deal)

Community
  • 1
  • 1
yuvi
  • 18,155
  • 8
  • 56
  • 93