Django: display many to many relationship through a model in template - django

I have this ManyToMany relationship through an intermediary model:
class Group(models.Model):
members = models.ManyToManyField(Student, through='NamedMembershipClub')
class Membership(models.Model):
year = models.IntegerField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
I'm trying to display members with the year they joined in my template.
I read on this post that I should use members_set.all
{% for member in object.members_set.all %}
<p>{{member.user.first_name}} {{member.year}}</p>
{% endfor %}
But it doesn't produce any output, the loop is just not entered because the set is empty.
I also tried :
{% for member in object.members.all %}
<p>{{member.first_name}}</p>
{% endfor %}
The view:
class DetailGroupView(TemplateView):
template_name = 'group/detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
self.object = Group.object.get(slug=kwargs['slug'])
context['object'] = self.object
return context
Which gives some satisfaction because I can display the user but not the associated data in the Membership model.
Is there any way to get the set directly in the template ? Thanks!
I'm running Django 3.0.

Related

Displaying get_context_data in template Django

I am trying to display the get_context_data on the template. I have a method on the model class that I need to call from ProfileView which has two different models. For the Profile View I have Profile Model and for the shippingaddress view I have ShippingAddress Model. And these models are from two different app. I tried the function below and it does not show any error, but when I tried to call it in the Template, It does not show the method.
Views.py
class ProfileView(LoginRequiredMixin, DetailView):
model = Profile
template_name = "account/profile.html"
success_url = "/"
def get_context_data(self, *args, **kwargs):
context = super(ProfileView, self).get_context_data(**kwargs)
context['shipping'] = ShippingAddress.objects.filter(user=self.request.user)
return context
Template code
{{object.get_full_address}}
Models.py
class ShippingAddress(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
phone_number = PhoneNumberField(null=True, blank=True)
street_address = models.CharField(max_length=300)
province = models.CharField(max_length=300)
city = models.CharField(max_length=50)
country = models.CharField(max_length=50)
zip_code = models.CharField(max_length=10)
def __str__(self):
return str(self.user)
def get_phone_number(self):
return self.phone_number
#property
def get_full_address(self):
return f"{self.street_address}, {self.province}, {self.city}, {self.country}, {self.zip_code}"
object is the context variable that DetailView will add to the context. For your view this would be an instance of Profile. You pass a queryset into the context with the name shipping so you can loop over that:
{% for shipping_address in shipping %}
{{ shipping_address.get_full_address }}
{% endfor %}
Note: You need to loop because one user has multiple Shipping Addresses according to your models.
Note: Also you didn't need to override get_context_data you could simply have written:
{% for shipping_address in request.user.shippingaddress_set %}
{{ shipping_address.get_full_address }}
{% endfor %}
Where shippingaddress_set is the related model name with _set
appended. You can change that by setting related_name on your
foreign key.

How to use ModelChoiceField in DRF?

I am trying to convert my form that was written earlier to django rest serializer but it does't work. Could you help me to solve this problem please?
this is my form:
class TripSearchForm(forms.Form):
departure = ModelChoiceField(
queryset=Place.objects.places_for_segment(), widget=autocomplete.ModelSelect2(url="autocomplete")
)
destination = ModelChoiceField(
queryset=Place.objects.places_for_segment(), widget=autocomplete.ModelSelect2(url="autocomplete")
)
How to built proper serializer?
class SearchSerializer(serializers.Serializer):
departure = serializers.RelatedField(queryset=places_models.Place.objects.all(),
label='departure')
destination = serializers.RelatedField(queryset=places_models.Place.objects.all(),
label='destination')
Assuming you have model Country
class Country(models.Model):
name = models.CharField(max_length=60, blank=True, default='')
You could write a serializers based on that
class CountryField(serializers.PrimaryKeyRelatedField):
def display_value(self, instance):
return instance.name
class CountrySerializer(serializers.ModelSerializer):
country = CountryField(queryset=Country.objects.all())
class Meta:
model = Country
fields = ('name', )
class DesiredSerializer(serializers.Serializer):
country = ColorSerializer()
Now you should be able to use your desired serialized to render choices from model either as html form for instance or whatever logic fits you better
if you want it as form
#views.py
def get(self, request):
serializer = DesiredSerializer()
return Response({ 'serializer': serializer }, template_name='my_model_choices_form.html')
<!-- my_model_choices_form.html -->
{% load rest_framework %}
<form action="..." method="POST">
{% csrf_token %}
{% render_form serializer %}
</form>
Now if you'll create instance of Country with some name it will be shown in select dropdown, display_value function can be used to customize the option output.
Hope that helps

Calling user follow/follower list in Django

Hi Djangonauts,
I am trying to get the user follows list(example like Instagram 'A' follows 'B') in the templates. I have tried almost everything I am not able to call it in the templates. What am I doing wrong
My models (It's a monkey patch to the Django User model)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
#other profile fields
class Contact(models.Model):
user_from = models.ForeignKey(User, related_name='supporter')
user_to = models.ForeignKey(User, related_name='leader')
def __str__(self):
return '{} follows {}'.format(self.user_from, self.user_to)
User.add_to_class(
'following',
models.ManyToManyField(
'self',
through=Contact,
related_name='followers',
symmetrical=False))
My views (not sure if this is correct)
def get_context_data(self, **kwargs):
context = super(ProfileView, self).get_context_data(**kwargs)
context['follows'] = Contact.objects.filter(
user_from__isnull=False,
user_from__username__iexact=self.kwargs.get('username'))
context['followers'] = Contact.objects.filter(
user_to__isnull=False,
user_to__username__iexact=self.kwargs.get('username'))
return context
My Templates
{{user}} follows {{user.supporter.count}} members #This works shows the correct number
{% for contact in Contact.objects.all %}
{{contact.user_to.username}} # I am not able to get this part to work
{% endfor %}
The bit that isn't working is the loop, because Contact is not defined in the template. But I don't understand why you think you need to access it. You should be looping through the many-to-many field on the user:
{% for follow in user.following.all %}
{{ follow.username }}
{% endfor %}

Django: DetailView containing reviews. Add a form for reviews with update/create

Currently I have the following structure.
I have Users, which can be Teachers or Students. Students can leave reviews for teachers. I've set up a detailed view for teachers and added a 'reviews' attribute to get_context_data to loop through reviews to display them.
Aim: Each user who is a student can submit a review of a teacher. I want to display a form at the bottom of my detailed view. If user already had a review, then we call update. If user doesn't have a review, we create it.
Models.py
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, related_name='Teacher')
availability = models.BooleanField(default=False)
def __str__(self):
return self.user.username
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, related_name='Student')
teacher = models.ForeignKey(Teacher)
reviewed = models.BooleanField(default=False)
def __str__(self):
return self.user.username
class Review(models.Model):
teacher = models.ForeignKey(Teacher)
student = models.OneToOneField(Student, on_delete=models.PROTECT, related_name='Student')
star = models.IntegerField(default=5)
body = models.TextField()
Views.py
class ReviewSubmitForm(forms.Form):
star = forms.IntegerField()
body = forms.CharField()
class TeacherView(generic.DetailView):
model = Teacher
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(TeacherView, self).get_context_data(**kwargs)
# Add extra context from another model
context['reviews'] = Review.objects.filter(teacher_id=self.kwargs['pk'])
context['rating'] = Review.objects.filter(teacher_id=self.kwargs['pk']).aggregate(Avg('star'))
context['form'] = ReviewSubmitForm()
return context
class ReviewSubmit(SingleObjectMixin, FormView):
template_name = 'users/teacher_detail.html'
form_class = ReviewSubmitForm
model = Review
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
return super(ReviewSubmit, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('users:detail', kwargs={'pk': self.object.pk})
I'm now able to see the form and use it in my template.
My teacher view is located at /users/<pk>
The logic that I'd like is: look for , check current student id.
If there is an entry with pk=pk and student_id = student_id then load update form. Otherwise if it's a student load create form.
This example is somewhat similar, but not quite the same.
Edit: form_template.html
{% for field in form %}
<div class="form-group">
<span color="red">{{ field.errors }}</span>
<label>
{{ field.label_tag }}
</label>
<div>{{ field }}</div>
</div>
{% endfor %}

How to let user choose which table needs to be updated Django

I have a form in my website, which is the same for three tables (Homework, Class, Random)
So basically I want to make a ChoiceField on the top of the form, to let user choose where to upload file.
I was thinking, because these tables have common abstract class, may be I can choose it from there somehow. But can not figure out how.
Or may be there is much better solution for this.
just in case this is my code:
#models.py
class FileDescription(models.Model):
class Meta:
abstract = True;
ordering = ['file_creation_time']
subject = models.ForeignKey('Subjects', null=True, blank=True, primary_key=True)
subject_name = models.CharField(max_length=100)
file_uploaded_by = models.CharField(max_length=100)
file_name = models.CharField(max_length=100)
file_description = models.TextField()
file_creation_time = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return u'%s' % (self.file_name)
#template
<ul id="tabs">
<li>Homework</li>
<li>Class Papers</li>
<li>Random Papers</li>
</ul>
<div id="homework" class="tab-section">
<h2>Homework</h2>
<p>This section contains Homework</p>
{% if homework_files %}
<ul>
{% for file in homework_files %}
<li>{{ file.file_name }}
{% endfor %}
</ul>
{% endif %}
</div>
#forms.py
class Homework_Content_Form(forms.ModelForm):
class Meta:
model=Homework_Content
exclude=('subject',
'subject_name',
'file_creation_time',
'file_uploaded_by',
)
Method 1: Separate Forms
class GenericContentForm(forms.ModelForm):
class Meta:
exclude = ('subject', 'subject_name', 'file_creation_time', 'file_uploaded_by')
TYPE_CHOICES = (
('homework', 'Homework'),
('class', 'Class Paper'),
('random', 'Random Paper'),
)
type = forms.ChoiceField(choices=TYPE_CHOICES)
class HomeworkForm(GenericContentForm):
class Meta(GenericContentForm.Meta):
model = Homework
class ClassPaperForm(GenericContentForm):
class Meta(GenericContentForm.Meta):
model = ClassPaper
class RandomPaperForm(GenericContentForm):
class Meta(GenericContentForm.Meta):
model = RandomPaper
Then in your view you just pick one to start with, and when you have the POST data, you can instantiate a different one instead:
def my_view(request):
if request.method == 'POST':
type = request.POST.get('type')
if type == 'homework':
form = HomeworkForm(request.POST)
elif type == 'class':
form = ClassPaperForm(request.POST)
elif type == 'random':
form = RandomPaperForm(request.POST)
else:
form = HomeworkForm()
...
Method 2: Use Proxy Models
Since these three models all share the same data, having three separate tables is redundant. Instead of FileDescription being abstract, make it just a normal standard model, and add a field to it for type, with choices of "Homework", "Class Paper" and "Random Paper". Then create proxy models for each:
class HomeworkManager(models.Manager):
def get_query_set(self, *args, **kwargs):
qs = super(HomeworkManager, self).get_query_set(*args, **kwargs)
return qs.filter(type='homework')
class Homework(FileDescription):
class Meta:
proxy = True
objects = HomeworkManager()
def save(self, *args, **kwargs):
self.type = 'homework'
super(Homework, self).save(*args, **kwargs)
Then, you just need one form for FileDescription and when the user's choice for the "type" will be saved. You can then access anything set as type "homework" with the standard Homework.objects.all().