4

Hello guys I'm trying to figure out how to add multiple addresses to a user. So the user can have shipping addresses and home addresses. I kind of guessed reading around but it's not working.

I also created a simple schema (I forgot to include the zipcode):

Screen-Shot-2019-02-08-at-7-31-36-PM.png

models.py

class Address(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    city = models.CharField(max_length=60, default="Miami")
    state = models.CharField(max_length=30, default="Florida")
    zipcode = models.CharField(max_length=5, default="33165")
    country = models.CharField(max_length=50)

    class Meta:
        verbose_name = 'Address'
        verbose_name_plural = 'Address'

    def __str__(self):
        return self.name

# All user data is/should be linked to this profile, so when user gets deleted, all data deletes as well
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    nick_name = models.CharField('Nick name', max_length=30, blank=True, default='')
    bio = models.TextField(max_length=500, blank=True)
    image = models.ImageField(default='default.jpg', upload_to='profile_pics')
    addresses = models.ManyToManyField(
        Address,
        through='AddressType',
        through_fields=('address', 'profile'),
    )

    # If we don't have this, it's going to say profile object only
    def __str__(self):
         return f'{self.user.username} Profile'  # it's going to print username Profile

    def save(self, *args, **kwargs):
            super().save(*args, **kwargs)

            img = Image.open(self.image.path)

            if img.height > 300 or img.width > 300:
                output_size = (300, 300)
                img.thumbnail(output_size)
                img.save(self.image.path)


class AddressType(models.Model):

    HOME_ADDRESS = 1
    SHIPPING_ADDRESS = 2

    TYPE_ADDRESS_CHOICES = (
        (HOME_ADDRESS, "Home address"),
        (SHIPPING_ADDRESS, "Shipping address"),
    )

    address = models.ForeignKey('Address', on_delete=models.CASCADE)
    profile = models.ForeignKey('Profile', on_delete=models.CASCADE)

    # This is the field you would use for know the type of address.
    address_type = models.PositiveIntegerField(choices=TYPE_ADDRESS_CHOICES)


When I do makemigrations it says:

ERRORS:
users.Profile.addresses: (fields.E339) 'AddressType.address' is not a foreign key to 'Profile'.
        HINT: Did you mean one of the following foreign keys to 'Profile': profile?
users.Profile.addresses: (fields.E339) 'AddressType.profile' is not a foreign key to 'Address'.
        HINT: Did you mean one of the following foreign keys to 'Address': address?

Can someone give me a hand please?

Thank you so much

1 Answers1

5

First a comment about your design ...

So a user can have many addresses, the difference is that can be a home or address or shipping address.

You could use a ManyToManyField and "describe" the relationship through a third model that will have the info whether is shipping or a home address.

First I would rename your 'HomeAddress' to 'Address' so is more semantic, then establish the relationship using through to a third table.

Read ManyToManyFiled docs for more detail.

Example:

class Address(models.Model):
    # ...

class Profile(models.Model):
    addresses = models.ManyToManyField(
         'Address', 
         through='AddressInfo'
         through_fields=('address', 'profile')
    )
    # ...

class AddressInfo(models.Model):

    HOME_ADDRESS = 1
    SHIPPING_ADDRESS = 2

    TYPE_ADDRESS_CHOICES = (
        (HOME_ADDRESS, "Home address"),
        (SHIPPIN_ADDRESS, "Shipping address"),
    )

    address = models.ForeignKey('Address', on_delete=models.CASCADE)
    profile = models.ForeignKey('Profile', on_delete=models.CASCADE)

    # This is the field you would use for know the type of address.
    address_type = models.PositiveIntegerField(choices=TYPE_ADDRESS_CHOICES)

About creating the forms ...

You could then write the form for adding an address to some profile having into account the address type.

If you want to add several addresses at the same time, the recommendation is to use a FormSet or a ModelFormSet.

Raydel Miranda
  • 13,825
  • 3
  • 38
  • 60
  • wow that's another level model lol thanks Ray, question, how would I call the form/model in the views.py? Because with the Profile model is easy: `instance=request.user.profile` but how do I do it in a new model that is not that one? Thanks for help, I'm loving Django every day more –  Feb 09 '19 at 03:52
  • never mind I think I got it, I'm sick now so tomorrow when I feel better I'll give it a shot. Here explains what you were saying https://docs.djangoproject.com/en/2.1/topics/db/models/#extra-fields-on-many-to-many-relationships, tomorrow I'll give it a shot –  Feb 09 '19 at 04:25
  • 1
    Glad to help, come back any time if you get stuck. – Raydel Miranda Feb 09 '19 at 04:35
  • Good morning Ray, I was trying to do it now and it rises an error on the `through_fields=('address', 'profile')` line, it says SyntaxError: invalid syntax. I updated the code above so you can see exactly how I have it :) –  Feb 09 '19 at 16:25
  • Btw reading the doc I think I found a typo on your code, shouldn't it be `addresses = models.ManyToManyField( Address, ...` instead of `'Address', ..`? –  Feb 09 '19 at 16:59
  • I updated the code above, it seems to be partially correct now, but I'm getting an error: `users.Profile.addresses: (fields.E339) 'AddressInfo.address' is not a foreign key to 'Profile'. HINT: Did you mean one of the following foreign keys to 'Profile': profile? users.Profile.addresses: (fields.E339) 'AddressInfo.profile' is not a foreign key to 'Address'. HINT: Did you mean one of the following foreign keys to 'Address': address?` –  Feb 09 '19 at 17:27
  • bro ayudame en esto si puedes, de verdad te lo agradeceria. Mira ya le hice un update al model y lo tengo trabajando, lo que no se me hace guarda la informacion en el views.py, can you check pleaseeeeee? El codigo de arriba de la pregunta esta updated –  Feb 10 '19 at 06:36
  • 1
    Lo reviso hoy en la noche. – Raydel Miranda Feb 10 '19 at 22:26
  • Gracias! Based on your hints I was able to link the models and now I can sort of see somthing in my html but how can I display it correctly on the html? I updated my most resent code on top. Thanks Ray –  Feb 11 '19 at 01:02
  • If you post another question I would check it out, let's avoid to make several questions in one only post. ;) – Raydel Miranda Feb 11 '19 at 01:17
  • Ok, tomorrow I’ll give it another try, If I can’t I’ll post it –  Feb 11 '19 at 01:19
  • Hi Ray, I was doing some exams for school and I was not able to finish it, this week I'll be working on this full time, could you help me finish the model please? I have the updated version on the initial post on top, can you review it? I follow what you told me and it gives me errors, I post it there too –  Feb 27 '19 at 18:33
  • I also created like you told me another post for the view https://stackoverflow.com/questions/54912928/how-to-link-address-model-to-views but this post is still unsolved :/ –  Feb 27 '19 at 19:19