0

I know the question seems simple to answer, and that the answer would be in the API ref somewhere, but I'm a bit new to django and have poured through the API for days trying to get this done. Any help would be highly appreciated!

I'm creating a basic blogging app for my University's computer labs, and in this app you have two relevant pages. A locations page which displays information about each lab on campus (hours, location/building), and a resources page which displays information about the equipment in each lab.

I have my models.py file set up as shown (there are other models but these are the only relevant ones:

class Location(models.Model):
    name = models.CharField(max_length=100)
    computer = models.ForeignKey('Computer')
    printer = models.ForeignKey('Printer')

class Computer(models.Model):
    computer_location = models.ForeignKey('Location', related_name='lab_computers')
    computer_model = models.ForeignKey('HardwareModel')

class Printer(models.Model):
    printer_location = models.ForeignKey('Location', related_name='lab_printers')
    printer_model = models.ForeignKey('HardwareModel')

class HardwareModel(models.Model):
    brand = models.CharField(max_length = 100)
    model_num = models.CharField(max_length = 30)

My views.py file:

class LocationsView(generic.ListView): #This isn't the view to be concerned with
    template_name = 'blog/location_list.html'
    context_object_name = 'locations'
    queryset = Location.objects.all()

#This is commented out because i was trying to modify the view to allow extra context
#I'm aware that you can't have 2 views with the same name. Just assume only one is active
#class ResourcesView(generic.ListView):
#    context_object_name = 'locations'
#    queryset = Location.objects.all()

def ResourcesView(request):
    locations = Location.objects.prefetch_related('lab_printers').all()
    return render_to_response('blog/resource_list.html',
          {'locations': locations})

The template that I need to display this in is like this:

<table>
    <tr>
        <th>Lab</th>
        <th>Device</th>
        <th>Type</th>
        <th>Model</th>
    </tr>
    {% for location in locations %}
    <tr>
        <td rowspan="{{ location.lab_printers.count }}">{{ location.name }}</td>
        <td>Computer</td>
        <td>{{ location.computer.computer_model.brand }}</td>
        <td>{{ location.computer.computer_model.model_num }}</td>
    </tr>
    <tr>
        <td>Printer</td>
        <td>{{ location.printer.printer_model.brand }}</td>
        <td>{{ location.printer.printer_model.model_num }}</td>
    </tr>
{% endfor %}
</table>

The point of displaying it like this is the the University has strict guidelines on how information on the web pages are presented and they want everything in neat organized tables. The table needs to have a dynamic rowspan on the first column to support people adding or deleting computer/printer types through django admin.

I searched through StackOverflow and found a few issues similar to mine and they said to use prefetch_related to get the related objects in one batch query, however when I did this I began to get a TypeError "RelatedManager" is not an iterable when trying to access the printers in the template. I understand why this is happening. It's because the RelatedManager class handling the query does not return a list/array/tuple. I just don't know how in the heck I can access the information I need in the way I need it.

I did have it partially working with the commented-out Generic View, however when I use location.lab_printers.count The values came back inconsistent and didn't reflect the actual objects in the database, messing up the formatting, and resulting in missing td elements and inaccurate display.

Sorry if I'm rambling, this is just a complex problem and I have absolutely run out of ideas on how I should go about doing this.

Any and all help would be so very greatly appreciated. Thank you for your time!

Kyle L.
  • 594
  • 4
  • 26
  • Can you show what you get when you print? – Gocht Jun 11 '15 at 16:48
  • if you don't mind can you clarify what you mean by print? – Kyle L. Jun 11 '15 at 16:49
  • I mean in your template. – Gocht Jun 11 '15 at 16:50
  • when I print out `location.lab_printers.count, I get: 0 0 2 – Kyle L. Jun 11 '15 at 16:52
  • correction: I get 2 0 0 – Kyle L. Jun 11 '15 at 16:54
  • 1
    You could try with `Location.objects.all().prefetch_related('print')` – Gocht Jun 11 '15 at 16:55
  • 1
    Or if you want the count for printers in a Location, you could use `Printer.objects.filter(printer_location=Location.objects.get(pk=location_id)).count()` – Gocht Jun 11 '15 at 16:59
  • in your first comment, you used 'print' as the argument to prefetch_related. Where does that name come from? I'm trying to understand how django handles the automatic naming for querysets – Kyle L. Jun 11 '15 at 17:06
  • Also with using your second suggestion, don't you need an argument in the request to specify the location_id in `objects.get()`? – Kyle L. Jun 11 '15 at 17:08
  • I meant to say 'printer', sorry. From Location attribute. – Gocht Jun 11 '15 at 17:08
  • `location_id` is an example, I assume you already have a location object and then you could query for Printer objects that belongs to that location object. – Gocht Jun 11 '15 at 17:10
  • I tried your first suggestion and it improved it a slight bit, however it's not working as intended. Here is a screenshot to show you the output, hopefully it's worth a thousand words! [screenshot](http://i224.photobucket.com/albums/dd18/Westwood_All_Day/Capture3.png) – Kyle L. Jun 11 '15 at 17:17
  • What you want to get when you print out `location.lab_printers.count`? – Gocht Jun 11 '15 at 17:23
  • 2 0 0 is what I got when I did suggestion #1. – Kyle L. Jun 11 '15 at 17:30
  • I have three locations. location1, location2, and location3. All three have at least one printer, but only the first location seems to get an accurate count. – Kyle L. Jun 11 '15 at 17:31
  • I just added a printer to location2 and the count changed to 2 1 0, which means that it is pulling the data correctly, but the count seems to be one off. Do you know a way of simply adding 1 to the value of count in the template? – Kyle L. Jun 11 '15 at 17:33
  • I looked in the API ref for template filters and found the `value|add:"1"` pipe, and that worked. I think if I can find a way to iterate through the printers for each location with each adding a row to the table it would work. However I can't iterate through prefetch_related because I get the TypeError. – Kyle L. Jun 11 '15 at 17:38
  • Your data model seems suspect. Why are there ForeignKeys from Location to Computer and Printer, but also FKs from Computer and Printer back to Location? – Daniel Roseman Jun 11 '15 at 17:38
  • Maybe @DanielRoseman can give us a class. I am testing this with a model in my database with a FK and I get one object but if I count I get 0... – Gocht Jun 11 '15 at 17:40
  • Daniel: because I can't find a way to reference each printer through the locations object without an FK relation. If there is a better way, please let me know. I knew it was clunky and probably not the best way from the start – Kyle L. Jun 11 '15 at 17:41
  • @Gocht here is another screenshot to show anyone else reading the desired output from the already existing application being used. [screenshot](http://i224.photobucket.com/albums/dd18/Westwood_All_Day/Capture4.png) – Kyle L. Jun 11 '15 at 17:43
  • But is there one computer/printer per location, or many? Because you've got it both ways, which makes no sense. If it's many, drop the one on Location and do `location.lab_printers.all()`. – Daniel Roseman Jun 11 '15 at 17:44
  • The way I intended it to work is for the Computer and Printer models to just be "templates". I just need the Computer model for displaying things like speed/memory but I need the Printer model to iterate {number of printers in one lab } per lab to add a row for each printer in the html table – Kyle L. Jun 11 '15 at 17:48
  • But I guess computers and printers belongs to just one location, so you don't need a FK from Location to Computer and Printer classes. – Gocht Jun 11 '15 at 17:56
  • I think I'm just not explaining it correctly? There should only be one Computer object, because the number of computers is just an IntegerField() in Location. But Printers are different since I have to iterate through the printers in each lab to the output. Let's say location1 has three printers. A black/white printer, a color printer, and a duplex printer. There will be 3 rows one for each printer in each location. – Kyle L. Jun 11 '15 at 18:01

1 Answers1

1

Not the answer, but use ManyToManyField for Location-Computer and Location-Printer relations. Docs link1, link2.

f43d65
  • 2,264
  • 11
  • 15