Linked linked to Profile model

Hi,

I am trying to work with different user profiles where the user can link products to there profile to display on a dashboard.

I have created 3 models:

class Product(models.Model):

    product_name = models.CharField(max_length=50, blank=False, unique=True)
    product_slug = models.CharField(max_length=50, blank=True,null=True)
    product_symbol = models.CharField(max_length=50, blank=True,null=True)
    product_price = models.FloatField(blank=True,null=True)


    def __str__(self):
        return str(self.product_name)
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)
    product_list = models.ManyToManyField(Product,  blank=True)
    website = models.URLField(max_length=50, blank=True)
    twitter = models.CharField(max_length=50, blank=True)
    meta = models.CharField(max_length=50, blank=True)

    def __str__(self):
        return str(self.user
class Holding(models.Model):

    product_name = models.ForeignKey(Product, on_delete=models.CASCADE)
    profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
    product_price = models.FloatField(max_length=50, blank=True, null=True)

    def __str__(self):
        return str(self.product_name)

I’ve manually added a product to the user profile which works, but im trying to show the product price that the user paid from the holding model

holdings = Holding.objects.get(profile_id=profile.id)

    profile = Profile.objects.get(user = request.user)
    products = profile.product_list.all()

Is this apprach with the models the correct way? How do i retrieve the holding data?

Thanks

If you look carefully, Holding is actually the through table of a ManyToMany relationship.

You may find some operations easier to visualize and code if you construct your models to take advantage of that.

Thanks Ken.

I know we’ve been through this before, but im not sure how to work this as a through table. Would that be changing the FK?

Thanks

Nope. Absolutely nothing changes within the Holding table - it’s perfectly fine as written. All you’re doing is adding a ManyToMany field on one of the other two tables identifying Holding as the through table.

Ok, so adding manytomany from Profile using something like product_price
product_price = models.ManyToManyField(Product, blank=True)?

Close. You forgot to add the through attribute.

members = models.ManyToManyField(Person, through=‘Membership’)
Profile
product_price = models.ManyToManyField(product_price, through=Holding) ?

I don’t know what this line is about:

But in Profile, you already have:

This already defines the ManyToMany between Profile and Product.
All you need to do is add the through attribute to this.

Sorry Ken, this members = models.ManyToManyField(Person, through=‘Membership’) was from the Django page, i pasted by mistake.

I see.
Thanks, Ken.

Tom.

It throws this error

Cannot alter field app.Profile.product_list into app.Profile.product_list - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)

Have i misunderstood - should i be creating a new field in Profile called product_price
product_price ... (Product, blank=True, through="Holding")

No, it’s just saying that you can’t migrate from one form to the other. This needs to be done with a clean slate.

You can do it by adding the new field, copying the data from the old table to the new, and then removing the old field. You just can’t switch from “non-through” to “through” by a simple makemigrations / migrate.

Thanks for your help as always Ken.

Tom

Sorry Ken, how do i retrieve values in Holdings via the through?

What are you starting from?

In other words, what object do you have from which you want to retrieve those values in Holdings?

This is my view that i am building out

@login_required
def index(request):
    profile = Profile.objects.get(user = request.user)
    products = profile.product.all()
    holdings = profile.products # Want to get price from Holding for this profile.
    return render(request, 'index.html',{'products':products})

Ok, you’re starting out from a Profile (profile)

So, the Products associated with that profile would be profile.product_list.all(). Likewise, the set of Holdings associated with that profile would be profile.holdings_set.all().

Now, if you want data from both Holdings and Products, you can retrieve the set of Holdings, and then use the product_name field for each Holdings to get the related Product.

Or, you can query for the holdings_set, and then annotate the result set with values from the related Product.

I like to list out on the dashboard the product and the price. So i think this is the way to go.

so i have products = profile.product_list.all() and holdings = profile.holdings_set.all()

but im not sure im following where to use product_name? and then within my template i would do

{% for item in holdings %}
{{item.product_name}} {{item.product_price}}
...

The problem with doing this is that there’s no correlation between the extra fields in holdings and the data in `products.

You only want holdings = profile.holdings_set.all() so that when you iterate over holdings using:

you can access the related field using the product_name field in the item instance.

Just don’t lose sight of the fact that you’ve got a poor name for the references to Products within Holdings. The product_name field in Holdings is not the name of the product, it’s the FK reference to the Products object.

Thanks Ken. Works perfectly :slight_smile:

On that. What would be a more appropriate name?

product perhaps?
That way, given a holding object, the name would be holding.product.product_name.