0

I'm new to Django and programming overall. So far, I was able to build a website just by referring to the official docs, but now I'm stuck. I'll describe my problem using gas stations analogy.

This is what I got:

class Grade(models.Model):
    grade_name = models.CharField(max_length=30)

class Station(models.Model):
    station_name = models.CharField(max_length=30)
    gas_grade = models.ManyToManyField(Grade)

With that I create three grade objects: Regular, Plus, Premium. After that I create a few stations to which I "assign" any combination of grades. Easy.

The problem starts when I need prices. An obvious solution would be adding Price model:

class Price(models.Model):
    station = models.ForeignKey(Station, on_delete=models.CASCADE)

    price_regular = models.CharField(max_length=30)
    price_plus = models.CharField(max_length=30)
    price_premium = models.CharField(max_length=30)

But adding the last three fields manually is no fun, to say the least. My "grades" and "stations" can grow daily. They also have two fields representing min/max price.

So... can I dynamically create fields based on existing objects? The point is I need each new station to have a price field for each grade assigned.

Please advise of any of my options.

It's almost a week since I'm beating the dead horse. Maybe I'm far from figuring this out because the logic I'm following is wrong?

Maybe a dedicated "add station" form outside of Django admin could work? But I assume that would require some good amount of javascript involved.

Thank you.

1
  • 1
    This is a database normalization problem that is well researched and understood. Take a few hours and study the subject, it is well worth. Commented May 7, 2017 at 21:10

2 Answers 2

3

You can think of grade and price as a property of a product offer from stations:

class Station(models.Model):
    station_name = models.CharField(max_length=30)


class ProductOffer(models.Model):
    GRADE = (
        ('regular', 'Regular'),
        ('plus', 'Plus'),
        ('premium', 'Premium'),
    )
    station = models.ForeignKey(Station)
    value = models.DecimalField()
    grade = models.CharField(max_length=30, choices=GRADE)

The above has a few advantages:

  • uses constants for gas grades in order to avoid a model for something with low change rate and low cardinality like gas types. This avoids an extra table and extra join.
  • a single foreign key avoids a many-to-many (among other inconveniences they use an intermediate table under-the-hood so again an extra table and extra join)
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for your input. Unfortunately, there is a couple of issues with this solution. 1) it's impossible to reuse GRADEs; 2) I can only select one grade, while a station can have any number of those; 3) the value will be set only once despite the number of grades offered. while the point is to give value to each grade related to the station.
1) Why do you think it is the case? 2) You can have many product offers for a single station (perhaps it is a good idea to set a unique_together for station and grade on the ProductOffer model). 3) Once you figure out why #1 is not an issue #3 will be clear as well.
the problem is, in 'real' environment grades can change once a week. more to that, each grade must have it's own page with 1) a description 2) the list of appropriate stations, i.e. page "grade x" lists all stations that offer grade x. the initial code is simplified, but it has a slug for an appropriate url. I realize it's getting confusing, and I also think that maybe I'm asking too much from Django. I'm pretty sure that what I need is doable, but only in a stop-gap/kludge way.
If your cardinality/change-rate is not low enough to justify using constants, you can create a Grade model and make make the ProductOffer.grade field a foreign key pointing to this model.
0

You need to use an intermediate model with your ManyToManyField. The price data goes here. So:

class Grade(models.Model):
    grade_name = models.CharField(max_length=30)

class Station(models.Model):
    station_name = models.CharField(max_length=30)
    gas_grade = models.ManyToManyField(Grade, through='StationGrade')

class StationGrade(models.Model):
    station = models.ForeignKey(Station, on_delete=models.CASCADE)
    grade = models.ForeignKey(Grade, on_delete=models.CASCADE)

    price = models.CharField(max_length=30)

2 Comments

thank you for the proposed solution. the problem is when I have 10 grades and 100 stations it gets obscure. I tried to limit the options, but without any success. The best I got was changing the station ID manually via limit_options_to, too bad you can't access ID from the model.py. I tried other options which I found on the Net, but none of them worked. I blame my Django incompetence. another problem is that StationGrade needs a name, otherwise CRUD operations become a nightmare. I still got high hopes for an "outside admin page", but only when I become more familiar with JSON/JS.
actually, I played around and now it's looking somewhat elegant and working. still need to figure out how to limit the choices, but after numerous attempts it gives the end result as expected. thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.