0

I would like to show possible choices from ManyToManyField (which I have in Homes model) in the Owners form. I have Owners <--Many2Many--> Homes with custom class HomesOwners. In Homes it works out of the box, I don't know how to make it work in Owners.

I am using Django 2.2.4 with Bootstrap 4 and Postgresql. I started my project based on django-bookshelf project (also just Django and Bootstrap4). I do not use any render. Comment in django-bookshelf project mentioned How to add bootstrap class to Django CreateView form fields in the template?, so I stick to that if it comed to forms.

I'm pretty new to Python (so Django too) and web technologies in general. I googled dozen of different questions/answers but I couldn't find any nice explanation of what is what and how to use it in real life. Most of them ended up with basic usage.

I did some experimentation on my own, but no success so far...



Here is the code

I have two models - Homes/models.py and Owners/models.py

Homes/models.py:

class Homes(models.Model):
   id = models.AutoField(primary_key=True)
   # other fields
   some_owners = models.ManyToManyField(Owners, through='HomesOwners', through_fields=('id_home', 'id_owner'), related_name='some_owners')
   # end of fields, some other code in the class like "class Meta" etc.

class HomesOwners(models.Model):
   id = models.AutoField(primary_key=True)
   id_home = models.ForeignKey(Homes, models.DO_NOTHING, db_column='id_home')
   id_owner = models.ForeignKey('owners.Owners', models.DO_NOTHING, db_column='id_owner')

Owners/models.py do not have anything special, no imports from my Homes/models.py etc.


and my forms:

Homes/forms.py:

class HomesForm(forms.ModelForm):
   def __init__(self, *args, **kwargs):
      super(HomesForm, self).__init__(*args, **kwargs)
      self.fields['some_field_from_homes_model'].widget.attrs = {'class': 'form-control '}
      #
      # --> no need self.fields for M2M, Django does the work
      #
      # but I tried also and have a --> Question 2
      # self.fields["some_owners"].widget = forms.widgets.CheckboxSelectMultiple()
      # self.fields["some_owners"].queryset = HomesOwners.objects.all()

Without any code as "self.fields" for M2M field, Django is able to generate for me list of owners.



Question 1

I would like to get list of Homes in my OwnersForms.

I do not know what to add. I assume that I cannot add

# Owners/models.py
some_homes = models.ManyToManyField(Homes, through='HomesOwners', through_fields=('id_home', 'id_owner'), related_name='some_homes')

because of circular import, am I right?

How do I get my Homes list using self.fields? What do I need to add to my code?



Question 2

When I've added

# Homes/forms.py
self.fields["some_owners"].widget = forms.widgets.CheckboxSelectMultiple()
self.fields["some_owners"].queryset = HomesOwners.objects.all()

I got

<!-- html page -->
HomesOwners object (1)
HomesOwners object (2)
<!-- and so on... -->

How can I just list Owners? How to filter/order them so first they would appear Owners not connected to any Home?



Question 3

class HomesOwners(models.Model):
   id = models.AutoField(primary_key=True)
   id_home = models.ForeignKey(Homes, models.DO_NOTHING, db_column='id_home')
   id_owner = models.ForeignKey('owners.Owners', models.DO_NOTHING, db_column='id_owner')

def __str__(self):
   return pass #return something

I can't get my head around this. This class connects Homes and Owners. When I'm thinking of Homes I would like to return Owners and vice versa. So it should return different things depending on what object we are using (home or owner). I think this is connected to my 2nd question about:

HomesOwners object (1)

Also...

In homes.html I'm using my M2M like that:

{% for owner in homes.homesowners_set.all %}
{{ owner.id_owner.id }}
{% endfor %}

I would like to write something similar to my owners.html and list homes. This is connected to my previous question, I would like to have full answer if that's possible.

EDIT

With the answer given to me I was able to add Homes to OwnerUpdate view. I have views like that: owners/views.py

# List View
class OwnersList(ListView):
    model = Owners

# Detail View
class OwnersView(DetailView):
    model = Owners

# Create View
class OwnersCreate(CreateView):
    model = Owners
    form_class = OwnersForm
    # Setting returning URL
    success_url = reverse_lazy('owners_list')

# Update View
class OwnersUpdate(UpdateView):
    model = Owners
    form_class = OwnersForm
    success_url = reverse_lazy('owners_list')

# Delete View
class OwnersDelete(DeleteView):
    model = Owners
    success_url = reverse_lazy('owners_list')

What change do I need to make to be able to show in OwnersList Homes they own? In Homes DetailView I am able to show Owners. I would like to do the same for Homes' DetailView and Homes ListView.

1 Answers1

1

I don't really get it what you asking for,
but if I understand your question correctly,
I assume you want to add Homes list (not HomesOwners list) into your Owners form, right?

you can add extra field in your form like this:

class OwnersForm(ModelForm):
    # notice the queryset is 'Homes' not 'HomesOwners'
    homes = forms.ModelMultipleChoiceField(queryset=Homes.objects.all(), widget=forms.CheckboxSelectMultiple)

    class Meta:
        model = Owners
        fields = ('homes', 'your_other_fields',)

    # then you can access in init function too if you want
    def __init__(self, *args, **kwargs):
        super(OwnersForm, self).__init__(*args, **kwargs)
        self.fields['homes'].required = False

then, since it using CheckboxSelectMultiple widget, you can iterate it in your html template like this:

{% for key, value in your_form.homes.field.choices %} <!-- notice '.field.'-->
{{ key }} = {{ value }}
{% endfor %}

you probably need to create custom save too for your form.


for your question 3, it is not about the form?
If you want to show HomesOwners, you are already doing right.

{% for owner in homes.homesowners_set.all %}
{{ owner.id_owner.id }}
{% endfor %}

but it will work if that homes is only 1 object.
if homes is a queryset, you have to iterate it first

{% for home in homes %}
    {% for owner in home.homesowners_set.all %}
    {{ owner.id_owner.id }}
    {% endfor %}
{% endfor %}

sorry if I misunderstanding your questions,
maybe you can provide your views.py too, so I or others can help you more specific

lieahau
  • 498
  • 4
  • 9
  • I just played with the first part, had to modify some things, but the main thing I think is working. Thank you so much for your input! –  Aug 12 '19 at 18:39
  • I edited my post adding one more question. Do you think you can help me with that too? –  Aug 12 '19 at 19:11
  • you can override `get_context_data` method in your view. see this [docs](https://docs.djangoproject.com/en/2.2/topics/class-based-views/generic-display/#adding-extra-context) – lieahau Aug 12 '19 at 23:28