Absolute beginner question -- recipes

221 views
Skip to first unread message

Simon Gunacker

unread,
Feb 28, 2016, 2:30:25 PM2/28/16
to Django users
Dear Django Community,

as a first django project I tried to write a site to manage cooking recipes. My model is quite simple:

* 1 recipe has n steps
* 1 step has a description and n ingredients, each of a certain amount defined by a certain unit

all in all, I have 5 tables (Recipe, Step, Ingredient, Unit, StepIngredient).

After reading through some documentation and trying around a little bit, I got my model and my admin site (I had to install django-nested-inline for the admin site to fit my expectations).

Now I am struggeling with my views. There is a simple index view wich basically lists all recipes:

class IndexView(generic.ListView):
    context_object_name
= 'recipes_list'

   
def get_queryset(self):
       
return Recipe.objects.all()

The hard part is setting up the details view. It is supposed to show the recipes name as a title and then list all the required ingredients and all the required steps. It tried something like:

def detail(request, pk):
    recipe
= Recipe.objects.get(id=pk)
    recipe
['steps'] = Steps.objects.filter(recipe_id=pk)
   
template = loader.get_template('recipe/recipe_detail.html')
    context
= {
       
'recipe': recipe,
   
}
   
return HttpResponse(template.render(context, request))

but it seems I am not allowed to modify the recipes object. Any ideas how to pass all the data which belongs to recipe to the template?

Thank you,

Simon
Message has been deleted

Rahul Gandhi

unread,
Feb 28, 2016, 2:47:31 PM2/28/16
to Django users
You could do something like this:

def detail(request, pk):
    recipe
= Recipe.objects.get(id=pk)

    steps
= Steps.objects.filter(recipe_id=pk)
   
template = loader.get_template('recipe/recipe_detail.html')
    context
= {
       
'recipe': recipe,

       
'steps': steps
   
}
   
return HttpResponse(template.render(context, request))

And make appropriate changes to your template. 

Simon Gunacker

unread,
Feb 28, 2016, 3:23:39 PM2/28/16
to Django users
Thank you Rahul,

I actually tried that an this is possible for the relationship between a recipe and its steps. But when it comes to the ingredients for a single step, I cannot do this any more since the ingredients depend on steps again. After reading your suggestion I thought about it once more and I came up with the following solution:

def detail(request, pk):
    recipe
= Recipe.objects.get(id=pk)

    steps
= Step.objects.filter(recipe_id=pk)

    ingredients
= dict()
   
for step in steps:
        ingredients
[step.id] = StepIngredients.objects.filter(step_id=step.id)

   
template = loader.get_template('recipe/rezept_detail.html')
    context
= {
       
'recipe': recipe,
       
'steps': steps,
       
'ingredients': ingredients
   
}
   
return HttpResponse(template.render(context, request))

However, when it comes to the view, it does not work any more:
<h1>{{ recipe.name }}</h1>

<table>
{% for step in steps %}

 
<tr>
   
<td>
    {% for ingredient in ingredients.step.id %}
      {{ ingredient.amount }}
    {% endfor %}
   
</td>

   
<td>{{ step.description }}</td>
 
</tr>

{% endfor %}
</table>

According to what I found on google, the django template language cannot deal with accessing dictionary entries using variables as keys. What is the django-way of solving this problem? Any ideas?

Thank you!

James Schneider

unread,
Feb 28, 2016, 9:38:35 PM2/28/16
to django...@googlegroups.com
On Sun, Feb 28, 2016 at 11:14 AM, Simon Gunacker <simon.g...@gmail.com> wrote:
Dear Django Community,

as a first django project I tried to write a site to manage cooking recipes. My model is quite simple:

* 1 recipe has n steps
* 1 step has a description and n ingredients, each of a certain amount defined by a certain unit

all in all, I have 5 tables (Recipe, Step, Ingredient, Unit, StepIngredient).

After reading through some documentation and trying around a little bit, I got my model and my admin site (I had to install django-nested-inline for the admin site to fit my expectations).

Now I am struggeling with my views. There is a simple index view wich basically lists all recipes:

class IndexView(generic.ListView):
    context_object_name
= 'recipes_list'

   
def get_queryset(self):
       
return Recipe.objects.all()


You can actually get rid of the get_queryset() override entirely here by properly setting the default view model to Recipe.

 
The hard part is setting up the details view. It is supposed to show the recipes name as a title and then list all the required ingredients and all the required steps. It tried something like:

def detail(request, pk):
    recipe
= Recipe.objects.get(id=pk)
    recipe
['steps'] = Steps.objects.filter(recipe_id=pk)
   
template = loader.get_template('recipe/recipe_detail.html')
    context
= {
       
'recipe': recipe,
   
}
   
return HttpResponse(template.render(context, request))

but it seems I am not allowed to modify the recipes object. Any ideas how to pass all the data which belongs to recipe to the template?


Whoa...why are you manually setting recipe['steps']? Isn't there already a relationship between Recipe (the model) and Step (Step should have a FK back to Recipe)? You should not need an explicit second call to Step.objects. If that call is necessary, then I would be highly suspicious that your models are not defined properly (sanely).

I would remove that second line where you set recipe['steps'] entirely. If that breaks something, your models are probably incorrect.

Assuming you run just the first line (recipe = Recipe.objects.get(id=pk)), you should be able to do the following in the Django shell:

recipe.steps # return a list of all of the steps associated with that recipe

recipe.steps[2].ingredients # return a list of ingredients for what is presumably step 3 of the recipe

There shouldn't be any need for manually querying for the steps or their ingredients after the original Recipe.objects.get() call. 

-James


James Schneider

unread,
Feb 28, 2016, 9:43:24 PM2/28/16
to django...@googlegroups.com
On Sun, Feb 28, 2016 at 11:47 AM, Rahul Gandhi <rahul.r...@gmail.com> wrote:
You could do something like this:

def detail(request, pk):
    recipe
= Recipe.objects.get(id=pk)

    steps
= Steps.objects.filter(recipe_id=pk)
   
template = loader.get_template('recipe/recipe_detail.html')
    context
= {
       
'recipe': recipe,

       
'steps': steps
   
}
   
return HttpResponse(template.render(context, request))

And make appropriate changes to your template. 


There's really no point is doing this. Within the template, {{ recipe.steps }} should already contain everything that {{ steps }} would contain in this instance. All you've done is created extra work for the ORM and added complication to the code.

Your context should only contain { 'recipe': recipe } at this point. Frankly, I'm not sure if it even needs that if you are using the built-in class-based views such as DetailView.

-James 

James Schneider

unread,
Feb 28, 2016, 10:09:46 PM2/28/16
to django...@googlegroups.com
On Sun, Feb 28, 2016 at 12:23 PM, Simon Gunacker <simon.g...@gmail.com> wrote:
Thank you Rahul,

I actually tried that an this is possible for the relationship between a recipe and its steps. But when it comes to the ingredients for a single step, I cannot do this any more since the ingredients depend on steps again. After reading your suggestion I thought about it once more and I came up with the following solution:

def detail(request, pk):
    recipe
= Recipe.objects.get(id=pk)

    steps
= Step.objects.filter(recipe_id=pk)

    ingredients
= dict()
   
for step in steps:
        ingredients
[step.id] = StepIngredients.objects.filter(step_id=step.id)

   
template = loader.get_template('recipe/rezept_detail.html')
    context
= {
       
'recipe': recipe,
       
'steps': steps,
       
'ingredients': ingredients
   
}
   
return HttpResponse(template.render(context, request))


Please review how the Django ORM works. You're creating much more work for yourself and your application. 50% of the code in this block can be removed.



def detail(request, pk):
    recipe 
= Recipe.objects.get(id=pk)

    template = loader.get_template('recipe/rezept_detail.html')
    context 
= {
        
'recipe': recipe,
    }

    
return HttpResponse(template.render(context, request))


I've already mentioned that separate calls to Step.objects are not necessary, because recipe.steps contains the results of such a query already.

The same applies to the ingredients for each step.

for step in recipe.steps:
    print('Ingredients: '.format(step.ingredients))
    # or
    for ingredient in step.ingredients:
        print(ingredient)

There is no need to have separate context variables within the template context for each piece of the recipe. All of the information that you are manually extracting is already available in the original object that was requested. The only exception that I would make is if you needed a separate list of all ingredients for all steps in one big list, which is much more difficult to extract. Even then, though, that sore of query is probably better handled by a model manager method that can be accessed via the template.

Of course, everything I'm saying is predicated on a decent model design. It could be that your current model structure doesn't allow for taking advantage of the ORM capabilities. If that's the case, though...you may want to review it.

 
However, when it comes to the view, it does not work any more:
<h1>{{ recipe.name }}</h1>

<table>
{% for step in steps %}

 
<tr>
   
<td>
    {% for ingredient in ingredients.step.id %}
      {{ ingredient.amount }}
    {% endfor %}
   
</td>

   
<td>{{ step.description }}</td>
 
</tr>

{% endfor %}
</table>

According to what I found on google, the django template language cannot deal with accessing dictionary entries using variables as keys. What is the django-way of solving this problem? Any ideas?

Yes, the template syntax is limited, intentionally. Assuming that you take my previous advice and utilize the recipe object directly:

<h1>{{ recipe.name }}</h1>

<table>
{% for step in recipe.steps %}

  
<tr>
    
<td>
    {% for ingredient in step.ingredients %}
      {{ ingredient.amount }}
    {% endfor %}
    
</td>


    
<td>{{ step.description }}</td>
  
</tr>

{% endfor %}
</table>

I have no idea why you are referring to ingredients.step.id in your template, it doesn't even make sense with the code you have (which is probably why things aren't behaving the way you'd expect). There really shouldn't be a need to ever refer to .id or .pk within a template, unless you are specifically printing out those values for an input field name or using {% url %}. 

Again, I'm making some assumptions about your models. If the above code snippets don't work (aside from typos), please post up your models.py file(s) so that we can adjust accordingly.

-James

Mike Dewhirst

unread,
Feb 28, 2016, 10:56:50 PM2/28/16
to django...@googlegroups.com
On 29/02/2016 2:09 PM, James Schneider wrote:
> Again, I'm making some assumptions about your models. If the above code
> snippets don't work (aside from typos), please post up your models.py
> file(s) so that we can adjust accordingly.
>

I think James is right. Maybe the answer to the above question is in
your subject line.

If I was doing a recipe app I would need to scratch my head. For
example, do I permit an ingredient to also be a recipe? In other words,
are ingredients simply raw materials or can they be separate
concoctions. I'm no chef but I suspect recipes include separately
prepared components which are brought together during the cooking
process or at the latest just before serving.

Maybe your "step" is a sub-recipe.

So the real question is about your expertise. If you are a chef all you
need here is how to do many-to-many relationships. There must be heaps
of manufacturer apps out there. They generally go under the label of
"bill-of-materials". It seems to me quite similar to recipes.

Anyway ...

I'm building a much simpler ingredients system with substances and
mixtures where a mixture is a substance having relationships with other
substances. Here is my setup:

class Substance(models.Model):
ingredients = models.ManyToManyField('self', symmetrical=False,
blank=True, through='Substance_Ingredients')
...

class Substance_Ingredients(models.Model):
substance = models.ForeignKey('Substance', null=True, blank=True,
related_name='base_substance')
ingredient = models.ForeignKey('Substance', null=True, blank=True,)
proportion = models.DecimalField(null=True, blank=True)
...

Hope this helps

Mike

Rahul Gandhi

unread,
Feb 29, 2016, 2:45:48 AM2/29/16
to Django users
I might be wrong here, but this is what I understand:

Let's say you have something like this:


class Recipe(models.Model):
    name
= models.CharField(max_length=255)
    description
= models.TextField()


class Step(models.Model):
    step_number
= models.IntegerField()
    description
= models.TextField()
    recipe
= models.ForeignKey(Recipe)


now, if I do the following in the shell:

recipe = Recipe.objects.get(id=1)
recipe
.steps

wouldn't this give an error?

To get the step, I could do something like
recipe.step_set

but I don't think I'll be able to do recipe.steps

James Schneider

unread,
Feb 29, 2016, 3:34:53 AM2/29/16
to django...@googlegroups.com
On Sun, Feb 28, 2016 at 11:45 PM, Rahul Gandhi <rahul.r...@gmail.com> wrote:
I might be wrong here, but this is what I understand:

Let's say you have something like this:


class Recipe(models.Model):
    name
= models.CharField(max_length=255)
    description
= models.TextField()


class Step(models.Model):
    step_number
= models.IntegerField()
    description
= models.TextField()
    recipe
= models.ForeignKey(Recipe)


now, if I do the following in the shell:

recipe = Recipe.objects.get(id=1)
recipe
.steps

wouldn't this give an error?

To get the step, I could do something like
recipe.step_set

but I don't think I'll be able to do recipe.steps


Given that model, you're right, sorry about that. My brain was moving faster than my fingers. All of my mentions of recipe.steps were meant as a reference to recipe.step_set. In my head I had already set the related_name to 'steps', like so:

recipe = models.ForeignKey(Recipe, related_name='steps')

This way you can use recipe.steps rather than recipe.step_set. Both are equivalent, but the custom related_name is easier to track for a human. The relation does act like its own manager though, so you would actually have to say recipe.steps.all() to get all of the steps (since a separate query is used to pull all of the steps). The template code would also have to be adjusted to include the .all (no parenthesis there). Also be sure to determine whether or not you should use prefetch_related for your list views (https://docs.djangoproject.com/en/1.9/ref/models/querysets/#prefetch-related).

However, this model does match what I assumed you had. I'm also assuming that you have a M2M relationship with Ingredient with a custom 'through' table so that you can match steps with ingredients and store other things like measurements on that relation? 

I would make a suggestion, though. Instead of using a 'step_number', you should look at using django-ordered-model instead. It does a very good job of keeping things, well, ordered. It also has other functionality like performing a renumber automatically if you decide that step 2 should actually be step 18 (and renumbers all of the other steps appropriately). The 'order' that is uses is zero-based, but you can add a simple property step_number that returns self.order + 1 (and sets the order correctly if you are renumbering by step). You'll also need to implement their 'order_with_respect_to', probably point it at the recipe field. 


-James

Simon Gunacker

unread,
Feb 29, 2016, 4:55:00 AM2/29/16
to Django users
Hey everybody,

thank you very much for your answers. I think I have already learned a lot from you! Well - there is quite a difference between stepping through a tutorial and trying to do something on my own. Regarding Mikes question whether a step of a recipe should be a sub-recipe. Well, as far as I know hierarchical relationships are hard to model in relational databases; that's why I kept my hands off! My decision to create a recipe app has a simple reason: I read some comparisons between django and ruby on rails and I decided to go for django since it seems to leave more freedom. I found a RoR tutorial on creating a recipe app some time ago and I am trying to do the same thing in django now.

Concerning my model - this is how it looks like:

from __future__ import unicode_literals

from django.db import models

class Unit(models.Model):
    abbreveation
= models.CharField(max_length=5, primary_key=True)
    description
= models.CharField(max_length=50)

   
def __str__(self):
       
return "%s (%s)" % (self.description, self.abbreveation)

class Ingredient(models.Model):
    name
= models.CharField(max_length=255)

   
def __str__(self):
       
return self.name

class Step(models.Model):
    name
= models.CharField(max_length=255, blank=True)
    recipe
= models.ForeignKey('Recipe', related_name='steps', on_delete=models.CASCADE, null=True)
    description
= models.TextField()
    ingredient
= models.ManyToManyField(Ingredient, through='StepIngredient')

   
def __str__(self):
       
if (self.name == ''):
           
return "Step %d" % self.id
       
else:
           
return "Step %d (%s)" % (self.id, self.name)

# additional information for many to many relationship between schritt and zutat
class StepIngredient(models.Model):
   
class Meta:
        unique_together
= (('step', 'ingredient'),)
    step
= models.ForeignKey(Step, null=True)
    ingredient
= models.ForeignKey(Ingredient, null=True)
    amount
= models.DecimalField(max_digits=7, decimal_places=3)
    unit
= models.ForeignKey(Unit, null=True)

   
def __str__(self):
       
return "%d %s %s" % (self.amount, self.unit.abbreveation, self.ingredient)


class Recipe(models.Model):
    name
= models.CharField(max_length=255)

   
   
# file will be uploaded to MEDIA_ROOT/images
   
# TODO: Upload works but link fails. Maybe sth. wrong with MEDIA_URL???
    photo
= models.ImageField(upload_to='images/', null=True)
    portionen
= models.IntegerField(default=2)

   
def __str__(self):
       
return self.name

Thank you James for pointing me at how to do my queries with django; I can finally list my steps using:

{% for step in recipe.steps.all %}

within my template. However, given the model above I am still not sure how to access a single steps ingredients ... any ideas or suggestions on my current model?

Thanks, Simon



James Schneider

unread,
Feb 29, 2016, 5:54:58 AM2/29/16
to django...@googlegroups.com


> Thank you James for pointing me at how to do my queries with django; I can finally list my steps using:
>
> {% for step in recipe.steps.all %}
>
> within my template. However, given the model above I am still not sure how to access a single steps ingredients ... any ideas or suggestions on my current model?

Sure, just have another nested {% for ingredient in step.ingredients.all %}

You should also change the 'ingredient' field to 'ingredients' in your Step model (reflected in my loop above). Makes it easier to read and indicate that you'll need the .all in the template (or elsewhere) if the attribute name is plural. Otherwise you are querying using step.ingredient, and expecting a list of multiple ingredients back.

-James

Simon Gunacker

unread,
Feb 29, 2016, 6:57:03 AM2/29/16
to Django users
Ok ... but how can I access the additional information stored in StepIngredient then?
And - to ask more general - is there a way to find all the "nested objects" attached to my current objects? Something to enter in the shell to see __all__ attributes; not just _meta.fields?

Mike Dewhirst

unread,
Feb 29, 2016, 7:01:04 AM2/29/16
to django...@googlegroups.com
On 29/02/2016 8:54 PM, Simon Gunacker wrote:
> Well, as far as I know hierarchical relationships are hard to model in
> relational databases;

Definitely incorrect. Hierarchical is merely a subset of relational.
Relational can handle hierarchical 1:n:m very easily plus n:1, n:m as well.

Relational databases are the most widely used today because they can
solve most real world modelling.

On that topic you should try to ignore database design initially and
instead try to understand the real world relationships between your data
types. Only then should you model the solution you need to provide.

I would be surprised if you couldn't solve most real world design
problems with 1:1, 1:n, n:1 and n:m relationships.

The closer your schema is to the real world, the easier it will be to
represent it in a relational (ie Django ORM) database. I believe best
practice is to ignore shortcuts aimed at performance initially and make
your design perfectly represent the real world. Only when it displays
performance problems not fixable with caching should you try and
introduce corner cutting in your design.

</rant>

Mike


Simon Gunacker

unread,
Feb 29, 2016, 7:51:26 AM2/29/16
to Django users
@Mike: the attempts (at least the ones I have seen) to store hierarchical data in relations [1] don't seem very intuitive to me. At least when it comes to query the data. Anyway: I would appreciate to be convinced by something else. However, I am afraid to kick off a completely different (yet interesting) discussion here. My current issue is how to access the additional fields of my M2M relationship. In addition to that I will think about hierarchical data in relational databases ;-)

Thanks, Simon

--

http://www.slideshare.net/billkarwin/models-for-hierarchical-data

Florian Schweikert

unread,
Feb 29, 2016, 8:02:52 AM2/29/16
to django...@googlegroups.com
On 28/02/16 20:14, Simon Gunacker wrote:
> |
> def detail(request, pk):
> recipe = Recipe.objects.get(id=pk)
> recipe['steps'] = Steps.objects.filter(recipe_id=pk)
> template = loader.get_template('recipe/recipe_detail.html')
> context = {
> 'recipe': recipe,
> }
> return HttpResponse(template.render(context,request))
> |

OT:
Have a look at the render shortcut:
https://docs.djangoproject.com/en/1.9/topics/http/shortcuts/#render

| return render(request, 'recipe/recipe_detail.html', context)

Maybe you can simply use a generic DetailView:
https://docs.djangoproject.com/es/1.9/ref/class-based-views/generic-display/#detailview

--
Florian

Simon Gunacker

unread,
Feb 29, 2016, 8:57:41 AM2/29/16
to Django users
Thank you Florian. After James pointed out that I can access my steps (and the ingredients in some way) by just passing the recipe object to the templates context, I thought of reducing the view to a DetailView again. But I have to somehow access the ingredients from my template for now ...

James Schneider

unread,
Feb 29, 2016, 7:26:18 PM2/29/16
to django...@googlegroups.com
On Mon, Feb 29, 2016 at 5:57 AM, Simon Gunacker <simon.g...@gmail.com> wrote:
Thank you Florian. After James pointed out that I can access my steps (and the ingredients in some way) by just passing the recipe object to the templates context, I thought of reducing the view to a DetailView again. But I have to somehow access the ingredients from my template for now ...


The related models using a 'through' table also have access to the 'through' table itself. With your current setup, I think you can do this:

<ul>
{% for steping in step.stepingredient_set.all %}
    <li>{{ steping.amount }} {{ steping.unit }} - {{ steping.ingredient }}</li>
{% empty %}
    <li>No ingredients needed for this step.</li>
{% endfor %}
</ul>

Optionally, for style points (and better human readability of the code), you can also add a 'related_name' parameter to the FK's in the 'through' table:

    step = models.ForeignKey(Step, null=True, related_name='step_ingredients')
    ingredient = models.ForeignKey(Ingredient, null=True, related_name='ingredient_steps')

That way, depending on the direction you are going, the call makes sense to a human:

<ul>
{% for steping in step.step_ingredients.all %}
    <li>{{ steping.amount }} {{ steping.unit }} - {{ steping.ingredient }}</li>
{% empty %}
    <li>No ingredients needed for this step.</li>
{% endfor %}
</ul>

I think that will work, although I haven't exactly tested this.

I'd highly recommend going with DetailView if you can. That makes your view a whopping 2 or 3 lines.

-James

Simon Gunacker

unread,
Mar 2, 2016, 11:16:25 AM3/2/16
to Django users
Thank you James. Taking your suggestion, I solved it!

Simon Gunacker

unread,
Mar 2, 2016, 11:22:04 AM3/2/16
to Django users
Inspired by Mike Dewhirsts suggestion on building hierachical structures, I've made up another model:
class Part(models.Model):
    parts
= models.ForeignKey('Part', null=True, blank=True, default=None, related_name='pieces')
    name
= models.CharField(max_length=200)

Then I made my view:
class PartsView(generic.ListView):
    context_object_name
='parts'
    model
=Part

But how should I design my template now?
<h1>Parts</h1>

<ul>
{% for part in parts %}
 
<li></li>
{% endfor %}
</ul>

I already found different answers on the net reaching from 'impossible' to 'with-tag' or 'install some add-ons'. According to Mikes former suggestion, I expected something easy and elegant here ...

regards, Simon


Mike Dewhirst

unread,
Mar 6, 2016, 10:17:39 AM3/6/16
to Django users
On Thursday, March 3, 2016 at 3:22:04 AM UTC+11, Simon Gunacker wrote:
Inspired by Mike Dewhirsts suggestion on building hierachical structures,


Not sure we are on the same page regarding "hierarchical". In the early days hierarchical databases only had 1:n relationships IOW foreign keys only. That is a very limiting thing. You have to turn your thinking inside out to successfully represent the real world.  That's what I meant. OTOH, RDBMS gives you everything you need to represent the real world.

 
I've made up another model:
class Part(models.Model):
    parts
= models.ForeignKey('Part', null=True, blank=True, default=None, related_name='pieces')
    name
= models.CharField(max_length=200)


For this to work in Django the table name should probably be 'self' rather than 'Part'. Haven't checked the docs on this recently.

Your 'parts' FK lets any Part be connected to exactly one or (null=True) no other part. Is that what your real world is? If so, why did you use plural nouns as the fieldname and related_name identifiers? Choosing meaningful names is very helpful when you revisit your code in future.

If your real world scenario is the "bill of materials" I mentioned earlier, you really need a many-to-many relationship between parts. That means any part could be in a number of different parts (ie assemblies) and assemblies themselves could actually be sub-assemblies in a finished product - which amounts to an assembly of sub-assemblies. If so, we might be getting close to your original recipes project.

Consider that what we need behind the scenes to represent a many-to-many relationship is a bunch of records in a "through" table (let's call it "Parts" plural) and all it needs is two FKs. Lets say fk1 points to the precise row in the Part table which happens to be an assembly or sub-assembly. fk1 in the through table would never point to a stand-alone part. Then fk2 would point to another row in the Part table. That other row is a part as well. It could represent a sub-assembly or a stand-alone part but never a complete assembly.

So when you want to know what all the parts are for an assembly you need a view which understands your business logic and can run the appropriate query to retrieve the results you want into a queryset, list or whatever. Assuming the result you want is a list of an assembly's parts, you would first identify the assembly in the Part table then query the "through" table (Parts) for all the rows with fk1==Part
 
 
Then I made my view:
class PartsView(generic.ListView):
    context_object_name
='parts'
    model
=Part


I have never tried generic views.

My typical approach is to create an arbitrary object (eg class Assembly(object)) and use its __init__() method to instantiate an assembly object with the queryset, list or whatever generated by the code in the view. In the hypothetical example I'm conjuring up here we would pass in Part.name as assembly.name and the queryset as assembly.qs (or similar identifiers).

 
But how should I design my template now?
<h1>Parts</h1>

<ul>
{% for part in parts %}
 
<li></li>
{% endfor %}
</ul>


<h1>Parts</h1>
<p>{{ assembly.name }}</p>
{% if assembly.qs %}
<ul>
{% for item in assembly.qs %}
<li>{{ item.fk2.name }}</li>
{% endfor %}
</ul>
{% else %}
<p>No parts</p>
{% endif %}

(each item represents a row in the through table Parts with fk2 being the sub-assembly or part)
 

That is all fine and dandy but what if you wanted more information about the particular spot the part needed to be placed or the way it should be aligned in the assembly? That information doesn't belong in the Parts table for the part concerned because it might be in many assemblies. Likewise it doesn't belong in the Parts table for the assembly concerned because there could be heaps and heaps of parts involved. It actually belongs in the "through" table Parts (plural). A single row in that table uniquely defines the relationship between assembly and part. If we add extra fields in the Parts "through" table those fields can carry whatever extra info is required.

Note that the queryset performed in the view retrieved records from the Parts table. That means if each row contains extra info you can simply show it in your template as follows ...

{% for item in assembly.qs %}
<li>{{ item.fk2.name }} Alignment {{ item.fk2.alignment }}</li>
{% endfor %}

Hope this helps

Mike
 


Mike Dewhirst

unread,
Mar 6, 2016, 5:29:08 PM3/6/16
to Django users
On 7/03/2016 2:17 AM, Mike Dewhirst wrote:
> On Thursday, March 3, 2016 at 3:22:04 AM UTC+11, Simon Gunacker wrote:
>
> Inspired by Mike Dewhirsts suggestion on building hierachical
> structures,
>
>
>
> Not sure we are on the same page regarding "hierarchical". In the early
> days hierarchical databases only had 1:n relationships IOW foreign keys
> only. That is a very limiting thing. You have to turn your thinking
> inside out to successfully represent the real world. That's what I
> meant. OTOH, RDBMS gives you everything you need to represent the real
> world.
>
> Â
>
> I've made up another model:
> |
> classPart(models.Model):
> Â Â parts
> =models.ForeignKey('Part',null=True,blank=True,default=None,related_name='pieces')
> Â Â name =models.CharField(max_length=200)
> |
>
>
> For this to work in Django the table name should probably be 'self'
> rather than 'Part'. Haven't checked the docs on this recently.
>
> Your 'parts' FK lets any Part be connected to exactly one or (null=True)
> no other part. Is that what your real world is? If so, why did you use
> plural nouns as the fieldname and related_name identifiers? Choosing
> meaningful names is very helpful when you revisit your code in future.
>
> If your real world scenario is the "bill of materials" I mentioned
> earlier, you really need a many-to-many relationship between parts. That
> means any part could be in a number of different parts (ie assemblies)
> and assemblies themselves could actually be sub-assemblies in a finished
> product - which amounts to an assembly of sub-assemblies. If so, we
> might be getting close to your original recipes project.
>
> Consider that what we need behind the scenes to represent a many-to-many
> relationship is a bunch of records in a "through" table (let's call it
> "Parts" plural) and all it needs is two FKs.

On rereading this, I should have said that for all the records (rows) in
the "bunch" the fk1 always points to the same primary key in the Part
table while every fk2 points to a different PK in the Part table.

Lets say fk1 points to the
> precise row in the Part table which happens to be an assembly or
> sub-assembly. fk1 in the through table would never point to a
> stand-alone part. Then fk2 would point to another row in the Part table.
> That other row is a part as well. It could represent a sub-assembly or a
> stand-alone part but never a complete assembly.

And further, I neglected to mention that Django manages all this for you
when you put ...

class Part(models.Model):
parts = ManyToManyField(null=True, blank=True, related_name=pieces")

... and working with this requires a few round-trips to the docs.

Mike

>
> So when you want to know what all the parts are for an assembly you need
> a view which understands your business logic and can run the appropriate
> query to retrieve the results you want into a queryset, list or
> whatever. Assuming the result you want is a list of an assembly's parts,
> you would first identify the assembly in the Part table then query the
> "through" table (Parts) for all the rows with fk1==Part
> Â ||
> Â
>
> Then I made my view:
> |
> classPartsView(generic.ListView):
> Â Â context_object_name='parts'
> Â Â model=Part
> |
>
>
> I have never tried generic views.
>
> My typical approach is to create an arbitrary object (eg class
> Assembly(object)) and use its __init__() method to instantiate an
> assembly object with the queryset, list or whatever generated by the
> code in the view. In the hypothetical example I'm conjuring up here we
> would pass in Part.name as assembly.name and the queryset as assembly.qs
> (or similar identifiers).
>
> Â
>
> But how should I design my template now?
> |
> <h1>Parts</h1>
>
> <ul>
> {% for part in parts %}
> Â <li></li>
> {% endfor %}
> </ul>
> |
>
>
> <h1>Parts</h1>
> <p>{{ assembly.name }}</p>
> {% if assembly.qs %}
> <ul>
> |{% for item in assembly.qs %}|
> <li>{{ item.fk2.name }}</li>
> {% endfor %}
> </ul>
> {% else %}
> <p>No parts</p>
> {% endif %}
>
> (each item represents a row in the through table Parts with fk2 being
> the sub-assembly or part)
> Â
>
> That is all fine and dandy but what if you wanted more information about
> the particular spot the part needed to be placed or the way it should be
> aligned in the assembly? That information doesn't belong in the Parts
> table for the part concerned because it might be in many assemblies.
> Likewise it doesn't belong in the Parts table for the assembly concerned
> because there could be heaps and heaps of parts involved. It actually
> belongs in the "through" table Parts (plural). A single row in that
> table uniquely defines the relationship between assembly and part. If we
> add extra fields in the Parts "through" table those fields can carry
> whatever extra info is required.
>
> Note that the queryset performed in the view retrieved records from the
> Parts table. That means if each row contains extra info you can simply
> show it in your template as follows ...
>
> |{% for item in assembly.qs %}|
> <li>{{ item.fk2.name }} Alignment {{ item.fk2.alignment }}</li>
> {% endfor %}
>
> Hope this helps
>
> Mike
> Â
>
>
> I already found different answers on the net reaching from
> 'impossible' to 'with-tag' or 'install some add-ons'. According to
> Mikes former suggestion, I expected something easy and elegant here ...
>
> regards, Simon
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at https://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/291d1c2a-627c-4765-8109-df02e84abdac%40googlegroups.com
> <https://groups.google.com/d/msgid/django-users/291d1c2a-627c-4765-8109-df02e84abdac%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

James Schneider

unread,
Mar 7, 2016, 5:02:03 PM3/7/16
to django...@googlegroups.com
On Mon, Feb 29, 2016 at 4:51 AM, Simon Gunacker <simon.g...@gmail.com> wrote:
@Mike: the attempts (at least the ones I have seen) to store hierarchical data in relations [1] don't seem very intuitive to me. At least when it comes to query the data. Anyway: I would appreciate to be convinced by something else. However, I am afraid to kick off a completely different (yet interesting) discussion here. My current issue is how to access the additional fields of my M2M relationship. In addition to that I will think about hierarchical data in relational databases ;-)


For Django-specific guidance, have a look at Django-treebeard, which has support for multiple hierarchical list strategies and a common API between each of the strategies.


Django-mptt (Modified Preorder Tree Traversal) also has some good resources:


There's also a host of other packages to manage trees of data:


-James
Reply all
Reply to author
Forward
0 new messages