How to Create A subscription button in Django - python

I'm new to web development and Python programming.
I want to create a button where users can subscribe to and unsubscribe from a sub-channel (much like a forum category) in a forum.
What I really want is for a user to be capable of subscribing and unsubscribing by clicking the button of the sub-channel.
views.py:
class SubChannelSubscriptionView(ListView):
template_name = 'subscription.html'
model = Topic
def get_queryset(self):
return SubChannelSubscription.objects.filter(user=self.request.user)
def get_context_data(self, **kwargs):
context = super(SubChannelSubscriptionView, self).get_context_data(**kwargs)
context['SubChannel'] = SubChannel.objects.all()
context['top'] = Topic.objects.filter(category__subchannel_subs__user=self.request.user)
return context
def subd(self, request):
subchannel = get_object_or_404(SubChannel, pk=self.kwargs['pk'])
is_subd = False
if subchannel.subd.filter(pk=request.user).exists():
subchannel.subd.remove(request.user)
is_subd = False
else:
subchannel.is_subd.add(request.user)
is_subd = True
return reverse('index')
models.py:
class SubChannel(models.Model):
title = models.CharField(max_length=150, unique=True, null=True)
description = models.CharField(max_length=150, null=True)
subd = models.ManyToManyField(User, related_name='subd' )
subscription.html:
<form action="{% url 'index' %}" method="post">
{% csrf_token %}
{% if is_subd %}
{% for sb in SubChannel %}
<button type="submit" value="" > subd{{ sb }} </button>
{% endfor %}
{% else %}
{% for sb in SubChannel %}
<button type="submit" value="" >not sub {{ sb }}</button>
{% endfor %}
{% endif %}
</form>

a simple way to toggle between subscription is to create a url for it and execute it by ajax.
this url, in its view, we get the user and channel, then we can find by querying that if the user is subscribed to channel if True then remove him else add that user to the channel.
I can't post any code for it because I didn't understand the logic. Here I noticing that SubChannel have ForaignKey channel and ManyToManyField of User.
I think there should be only two Models
Channel
User -> ManyToManyField Channel
By this one can easily manage user having multiple channels and all users subscribed to a channel. Adding a Third DBTable of UserChannel is done internally by Django ORM. (I understand that this may not be sutaible for your specific requirements.)

Related

Django - Auto populating Many-To-Many-Field relation

I am trying to make a form that auto populates a many-to-many relation for my user model. The goal is to have a submit button that adds the views instance object (the SingelWorkout object) to a many-to-many field relation within my user model.
The view accurately displays the correct object, and the form appears as intended within the template. I do not wish for the user to see the many-to-many field selection. Aside from the submit button, I am trying to have all logic to occur on the backend. How would I assign an object instance to a field within a form? Would this occur in the views.py or the forms.py?
Here is why my user model looks like:
class FitnessUser(AbstractUser):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField(max_length=60)
age_category = models.ForeignKey(
AgeGroup,
on_delete=models.CASCADE,
blank=True,
null=True
)
goal = models.IntegerField(default=1 ,choices=Purpose.choices)
weight = models.CharField(max_length=30)
height = models.CharField(max_length=30)
gender = models.ForeignKey(
Gender,
on_delete=models.CASCADE,
blank=True,
null=True
)
exercise_frequency = models.IntegerField(default=1 ,choices=Frequency.choices)
template_id = models.ForeignKey(
Workout_template,
on_delete=models.CASCADE,
blank=True,
null=True
)
completed_workouts = models.ManyToManyField(SingleWorkout)
def get_absolute_url(self):
return reverse('detail', args=[self.id])
This is my form in forms.py:
class CustomWorkoutChangeForm(UserChangeForm):
class Meta(UserChangeForm):
model = FitnessUser
fields = ('completed_workouts',)
exclude = ('completed_workouts',)
UserChangeForm.password = None
This is how my view looks:
class WorkoutUpdateView(LoginRequiredMixin, UpdateView):
model = SingleWorkout
template_name = 'workout/daily_view.html'
form_class = CustomWorkoutChangeForm
success_url = reverse_lazy("template")
def get_context_data(self, **kwargs):
context = super(WorkoutUpdateView, self).get_context_data(**kwargs)
context['workout'] = SingleWorkout.objects.get(slug = self.kwargs['slug'])
return context
My html template looks like this:
{{workout}}
<br>
workout:
<br>
{{ workout.exercise_1 }}
<br>
{{ workout.description_1 }}
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Confirm">
</form>
Figured out a solution. I created a view that gets the instance object based on the objects url slug, and also gets the user by its pk. From there is adds the instance object to the users many to many field, then redirects back to the previous page.
New view created:
def update_workout_data(request, slug):
workout = SingleWorkout.objects.get(slug=slug)
endUser = FitnessUser.objects.get(pk = request.user.pk)
endUser.completed_workouts.add(workout)
endUser.save()
return redirect(reverse('daily', kwargs={'slug':workout.slug}))
Updated HTML appearance. I've also altered the html and its detail view so that the update link will redirect to a separate update view, depending on the need to add/remove the relation.
{% block content %}
Daily View
<br>
{{exercise}}
<br>
workout:
<br>
<br>
{% if exercise.name in workouts %}
<h5>Workout Already Completed</h5>
<form method="POST" action="{% url 'remove' slug=exercise.slug %}">
{% csrf_token %}
<button type="submit">Reset</button>
</form>
{% else %}
<form method="POST" action="{% url 'update' slug=exercise.slug %}">
{% csrf_token %}
<button type="submit">Complete</button>
</form>
{% endif %}
{% endblock content %}
Updated Detail View
def get_context_data(self, **kwargs):
context = super(WorkoutDetailView, self).get_context_data(**kwargs)
user = FitnessUser.objects.get(pk = self.request.user.pk)
context['exercise'] = SingleWorkout.objects.get(slug = self.kwargs['slug'])
context['workouts'] = {}
for workout in user.completed_workouts.all():
context['workouts'][workout.name] = workout
return context

Django: Access related object attributes from a form field in a template?

I'm building a page that allows users to edit Task and related Activity records (one task can have many activities), all on the same page. Here are extracts from my code...
models.py
from django.contrib.auth.models import User
class Task(models.Model):
category = models.CharField(max_length=300)
description = models.CharField(max_length=300)
class Activity(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
notes = models.TextField(blank=True)
owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
The activity "owner" is linked to a User from the Django standard user model.
views.py
def manage_task(request, pk):
task = Task.objects.get(pk = pk)
TaskInlineFormSet = inlineformset_factory(Task, Activity,
form = ActivityForm)
if request.method == "POST":
form = TaskForm(request.POST, instance = task)
formset = TaskInlineFormSet(request.POST, instance = task)
if form.has_changed() and form.is_valid():
form.save()
if formset.has_changed() and formset.is_valid():
formset.save()
return redirect('manage_task',pk=task.id)
else:
form = TaskForm(instance = task)
formset = TaskInlineFormSet(instance = task)
context = {'task': task, 'task_form': form, 'formset': formset}
return render(request, 'tasks/manage_task.html', context)
And manage_task.html excerpt:
<h2>{{ task.category }}</h2>
<form method="post">
{% csrf_token %}
{{ task_form.description }}
{% for form in formset %}
{{ form.id }}
{{ form.title }}</br>
{{ form.notes }}</br>
{% if user.id == form.owner.value %}
You own this Activity!</br>
{% else %}
{{ form.owner.first_name }} owns this Activity</br>
{% endif %}
{% endfor %}
<input class="save" type="submit" value="SAVE">
</form>
Perhaps obviously, {{ form.owner.first_name }} doesn't return anything. form.owner understandably renders as a select drop down with all the Users listed - it's some sort of iterable object so it doesn't have a first_name attribute.
I think I need to have the first_name field in each form in the formset so it ties up properly with the correct Activity form.
I feel like I might have to adapt the view in some way to go through all the forms in the formset and use the owner.id (if it exists) to access the related User and add the first_name attribute as an extra field in the form somehow? That doesn't feel very Djangoish, there must be a better way.
How can I use the form field form.owner to get the first_name attribute from the User object that's related to the Activity so I can use it in the template?
You should not take such value from form directly. It may be risky (i.e. changing real-life). But if you are aware of it and ok with that, try pointing to specific instance of form:
{{ form.instance.owner.first_name }}

Posts not showing author who wrote it in Django

Basically, I'm writing an app in which people can make blog and image posts. So far, I've completed users to be able to write text posts. However, when I try to create a post, it returns "By: None" when it should be returning "By: shrey". In this case, Bob is the author. Here's an image:
Here's an image for the post creation view:
Theoretically, when I enter a post it should say who it was written by.
Here's the template for the create post:
{% extends "social/base.html" %}
{% load crispy_forms_tags %}
{% block content4 %}
<h1>Make Your Post</h1>
<p>Write a post / Share an image</p>
<br>
<div class="container">
<form method="post">
{% csrf_token %}
{{form|crispy}}
<button type="submit" name="button">Make Post</button>
</form>
</div>
{% endblock content4 %}
Here's the function for the create post view:
class PostCreateView(CreateView):
model = Posts
fields = ['post_title', 'post_text_content']
def form_valid(self, form):
form.instance.author = self.request.user
print(self.request.user)
return super().form_valid(form)
Thank you in advance.
EDIT: Home Page Template (template which displays the posts):
{% extends "social/base.html" %}
{% block content %}
<h1>Your Feed</h1>
<p>This is your feed. Here, you'll see posts from people you follow.</p>
{% if user.is_authenticated %}
<p>You are logged in as {{user.username}}. This is your feed.</p>
{% else %}
<p>You are not logged in. This is a random feed.</p>
{% endif %}
{% for post in posts %}
<h1>{{ post.post_title }}</h1>
<p>By {{ post.post_author }} on <i>{{ post.post_date }}</i></p>
<p>{{ post.post_text_content }}</p>
{% endfor %}
Click here to make a post.
<br>
Click here to logout.
<br>
Click here to login.
<br>
Click here to sign up and make an account.
<!--<p>Want to post something? Enter your info here: </p> -->
{% endblock content %}
Posts Model:
class Posts(models.Model):
post_title = models.CharField(max_length = 40, help_text = 'Enter post title')
post_text_content = models.TextField(max_length = 1000)
post_author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
post_date = models.DateField(auto_now = True, auto_now_add = False)
#Make optional Image Field
class Meta:
ordering = ['post_title', 'post_author', 'post_date', 'post_text_content']
def __str__(self):
return self.post_title
def get_absolute_url(self):
return reverse('social-home')
The name of the field is post_author, not author, hence you should set post_author:
class PostCreateView(CreateView):
model = Posts
fields = ['post_title', 'post_text_content']
def form_valid(self, form):
form.instance.post_author = self.request.user
return super().form_valid(form)
That being said, normally in Django one does not prefixes the model fields with the name of the model. One reason not to do that is that you can define abstract models where you define the field once, and then use inheritance to add the field to other models.

Django - Find out which model instance I'm editing in an inline formset

Note: I'm new to Django and Python. I'm not sure if I've misunderstood the use of inline formsets in some way.
I needed to be able to edit certain fields for multiple instances of a Guest model (linked to a parent Invite model) at the same time in one form. After a lot of back and forth, I managed to set up an inline formset that submits successfully.
However, the interface is confusing, because you don't know which Guest you're filling in details for. Each Guest has a guest_name field, and I just want to display that name for each form, so that users know who they're editing for.
Here's my (condensed) view:
def extra_view(request, code):
# Get the specific invite
invite = get_invite(code)
# Get the context from the request.
context = RequestContext(request)
# Store guests attending object
guests_attending = invite.guest_set.filter(attending=True, invite=invite)
# Create the formset for each Guest
GuestFormset = inlineformset_factory(Invite, Guest,
form=ExtraForm,
fields=('diet', 'transport'),
extra=0,
can_delete=False)
if request.method == "POST":
formset = GuestFormset(request.POST, request.FILES,
instance=invite,
queryset=Guest.objects.filter(attending=1))
if formset.is_valid():
# Save the data to the database.
formset.save()
# Redirect stuff here
if guests_attending.count() > 0:
formset = GuestFormset(instance=invite, queryset=Guest.objects.filter(attending=1))
# Return the view
return render_to_response('app/extra.html', {
'GuestForm': formset,
'invite': invite,
'guests_attending': guests_attending,
'errors': formset.errors
}, context)
else:
# Backup for if no guests are attending
Here's how the models look:
class Invite(models.Model):
# Code to identify invites by
code = models.CharField(max_length=6, default=code_generator(4, do_check=True), unique=True)
group_name = models.CharField(max_length=200)
def __str__(self):
return self.group_name
class Guest(models.Model):
invite = models.ForeignKey(Invite, on_delete=models.CASCADE)
guest_name = models.CharField(max_length=200)
diet = models.CharField(max_length=250, null=True, blank=True)
transport = models.NullBooleanField(default=False)
attending = models.NullBooleanField(default=False)
def __str__(self):
return self.guest_name
And here's my template
{% if invite %}
<form method="post" action="">
{% csrf_token %}
{{ GuestForm.management_form }}
<table>
{% for form in GuestForm %}
<!-- Which guest am I editing for?! -->
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
<div>
{{ field.help_text }}
{{ field }}
</div>
{% endfor %}
{% endfor %}
</table>
<button type="submit" class="btn">Continue</button>
</form>
{% endif %}
You can access the form's instance with form.instance. In the template, you could do something like:
{{ form.instance.guest_name }}

Django: Check that row exists in list

I want to check that user_id exists in the profile_images table from my Django template.
My Model
class profiles(models.Model):
profile_id = models.AutoField(primary_key=True)
user = models.ForeignKey(User)
-----
class Profile_images(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User)
image = models.ImageField(upload_to='uploads/',default = 'uploads/no-img.jpg')
My View
def view_profiles(request):
if request.user.is_authenticated():
view_all_profiles = profiles.objects.all()
profile_image = Profile_images.objects.all()
return render_to_response('profiles/all.html', {'profiles':view_all_profiles,'profile_image':profile_image}, context_instance=RequestContext(request),)
else:
return HttpResponseRedirect('/accounts/login/')
My Template
{% for profile in profiles %}
<li>
{% for image in profile_image %}
{% ifequal image.user_id profile.user_id %}
<img src="{{MEDIA_URL}}{{image.image}}" alt="image" />
{% endifequal %}
<!-- i want to check here if not user_id exist in profile_images table -->
{% if profile.user_id not in profile_image %}
<img src="{% static 'images/no-image.jpg' %}" alt="image" />
{% endif %}
{% endfor %}
</li>
{% endfor %}
{% if profile.user_id not in profile_image %} is not working. I'm new to Django & python and I'm stuck here. Please suggest better ways if my code is not correct.
in your view you could get all user_ids with a profile image, something like:
user_ids_with_profile_images = Profile_images.objects.all().values_list('user_id', flat=True)
Then in your template you could check if profile.user_id not in user_ids_with_profile_images.
It might actually be a little cleaner to loop through all the users with profiles in your system and get their profile images through the foreign key, instead of looping through all the profiles and trying to get the users...
This is really a design problem, you've got a separate model specifically for a profile image when that could just be a field on the profile model itself.
class Profile(models.Model): # convention is to use a non-plural name for models
# No need to explicitly set the primary key, this will be added automatically
# profile_id = models.AutoField(primary_key=True)
user = models.ForeignKey(User)
image = models.ImageField(upload_to='uploads/',default = 'uploads/no-img.jpg')
-----
Now it would just be a case of using {{ profile.image }} with no need for any additional looking up.

Categories