How can I access object in Mixin via dispatch? - django

So I am trying to use Mixin, and the aim is to check if the requester is the owner of the object (owner is a field in my model).
However, I am unable to do such a thing, with a result of 'TweetsUpdateView' object has no attribute 'object', what is wrong in my code?
My models
class Tweets(models.Model):
description = models.TextField(blank=True, null=False, default="", max_length=255)
createdAt = models.DateTimeField(auto_now_add=True, null=True, blank=True)
updatedAt = models.DateTimeField(auto_now=True)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name="owner_tweets_set",
)
user_likes = models.ManyToManyField(settings.AUTH_USER_MODEL)
My view
class OwnerRequiredMixin(UserPassesTestMixin):
def dispatch(self, request, *args, **kwargs):
if self.object.owner != self.request.user:
return HttpResponseForbidden()
return super(OwnerRequiredMixin, self).dispatch(request, *args, **kwargs)
class TweetsUpdateView(
LoginRequiredMixin,
OwnerRequiredMixin,
UpdateView,
):
model = Tweets
# fields = ["description"]
# template_name = "tweets_form.html"
template_name_suffix = "_form"
form_class = TweetForm
def form_invalid(self, form):
print("form is invalid")
return HttpResponse("form is invalid.. this is just an HttpResponse object")
# slug_field = "id"
# slug_url_kwarg = "tweet_id"
# success_url = reverse_lazy("tweets:twitter")
success_url = reverse_lazy("tweets:twitter")

you can access objects by using self.object = self.get_object()
as it hasn't been called yet

Related

Django change <a> tag on template depending on a condition

I have posts with the option of saving it. The view works properly but I can't see the change in the button. I want that when it is not saved by a user the button says "Save post" and when it is already saved the button should say "unsave". I'm getting problems with this last part. Here part of the code.
views.py
def post(request):
posts = Post.objects.all().order_by("-date_created")
return render(request, "post/food-feed.html", {"posts": posts})
def save_post(request, pk):
post = get_object_or_404(Post, id=pk)
if post.favorite_posts.filter(id=request.user.id).exists():
post.favorite_posts.remove(request.user)
else:
post.favorite_posts.add(request.user)
return HttpResponseRedirect(request.META["HTTP_REFERER"])
template
Save post
urls.py
path("post/<int:pk>/save", views.save_post, name="save-post")
forms.py
class UserSignupForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ["username", "email", "password1", "password2"]
def clean_username(self):
username = self.cleaned_data["username"].lower()
if not re.match(r"^[A-Za-z0-9_]+$", username):
raise forms.ValidationError(
"Sorry, your username must only contain letters, numbers and underscores."
)
elif (
User.objects.exclude(pk=self.instance.pk).filter(username=username).exists()
):
raise forms.ValidationError(f"Username {username} is already in use.")
else:
return username
models.py
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=80)
slug = models.SlugField(max_length=250, null=True, blank=True)
post_description = models.TextField(max_length=140, null=True, blank=True)
date_created = models.DateTimeField(default=timezone.now)
date_updated = models.DateTimeField(auto_now=True)
main_image = models.ImageField(upload_to="post_pics")
is_recipe = models.BooleanField()
ingredients = models.TextField(blank=True, null=True)
recipe_description = models.TextField(blank=True, null=True)
cooking_time = models.CharField(max_length=20, blank=True, null=True)
likes = models.ManyToManyField(User, related_name="post_likes")
loves = models.ManyToManyField(User, related_name="post_loves")
drooling_faces = models.ManyToManyField(User, related_name="post_drooling_faces")
favorite_posts = models.ManyToManyField(
User, related_name="favorite_posts", default=None, blank=True
)
def clean(self, *args, **kwargs):
if (
(self.is_recipe and self.ingredients == None)
or (self.is_recipe and self.ingredients == None)
or (self.is_recipe and self.recipe_description == None)
or (self.is_recipe and self.cooking_time == None)
):
raise ValidationError("You need to complete the recipe fields!")
if not self.slug:
slug_title = slugify(self.title)
slug_date = slugify(self.date_created)
self.slug = f"{slug_title}-{slug_date}"
super().clean(*args, **kwargs)
class Meta:
ordering = ("-date_created",)
def save(self, *args, **kwargs):
self.full_clean()
return super().save(*args, **kwargs)
def __str__(self) -> str:
return self.title
def get_absolute_url(self):
return reverse("post-detail", kwargs={"slug": self.slug})
def like_count(self):
return self.likes.count()
def love_count(self):
return self.loves.count()
def droolingface_count(self):
return self.drooling_faces.count()
Each post is shown in the feed-food template with user image, user username, date, photo, title, likes, description and THE SAVE BUTTON
What do I need to add to make the statement work?
You can do using queryset anotation or custom filter tag.
I don't know your models. but you can annotate is_favorite something like this in post view:
from django.db.models import Exists, OuterRef
Post.objects.annotate(
is_favorite=Exists(
Favorite.objects.filter(user=user, product_id=OuterRef('pk'))
)
)
in template:
{% if post.is_favorite %}
save
{% else %}
unsave
{% endif %}

Django: When form is submitted, auto-fill a field

I'm looking to auto-fill a field when I submit a form so it is identical to another field.
I have a createView with a modelForm. The idea is the user writes his/hers name, which then when submitted fills out the display_name field as well. I know it's weird "why have two of the same", but it has a purpose.
Here's my code - I'm guessing I need to create a context that gets the display_name and sets it to be equal to the name?
views.py
class CreateIdentity(CreateView):
template_name = 'admin/CreateIdentity.html'
model = Identity
form_class = CreateIdentityForm
queryset = Identity.objects.all()
def get_context_data(self, **kwargs):
context = super(CreateIdentity, self).get_context_data(**kwargs)
context["user_id"] = User.objects.get(username=self.request.user)
return context
def form_valid(self, form):
form.instance.user_id = self.request.user
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("view_identity_created_with_slug", kwargs={'slug': self.object.slug})
models.py
class Identity(models.Model):
name = models.CharField(max_length=200, null=False)
display_name = models.CharField(max_length=200)
slug = models.SlugField(max_length=100)
user_id = models.ForeignKey(User, db_column="username", on_delete=models.CASCADE, null=False)
forms.py
class CreateIdentityForm(forms.ModelForm):
name = forms.CharField(required=True, widget=forms.TextInput(attrs={
'spellcheck': "false",
"class": "identity_name_input",
'placeholder': "Write your identity"}),
)
class Meta:
model = Identity
fields = ["name"]
def __str__(self):
return self.name
In the .form_valid(…) [Django-doc], you can specify the display_name based on the cleaned data of the form:
class CreateIdentity(CreateView):
template_name = 'admin/CreateIdentity.html'
model = Identity
form_class = CreateIdentityForm
queryset = Identity.objects.all()
def get_context_data(self, **kwargs):
context = super(CreateIdentity, self).get_context_data(**kwargs)
context["user_id"] = User.objects.get(username=self.request.user)
return context
def form_valid(self, form):
form.instance.user_id = self.request.user
form.instance.display_name = form.cleaned_data['name']
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("view_identity_created_with_slug", kwargs={'slug': self.object.slug})

AttributeError at /addimam 'int' object has no attribute '_meta' Django

I got this error when I'm trying to submit data in my view. Masjid is taking data from a specific login user. I cannot understand where the error is coming because my form allow my to view the specific user.
form = ImamForm(request.POST, instance=masjid)
error message is coming from that line
forms.py
class ImamForm(forms.ModelForm):
def __init__(self,user, *args, **kwargs):
super(ImamForm, self).__init__(*args, **kwargs)
try:
masjid_id = Info.objects.values_list('user_id', flat=True).get(user=user)
self.fields['masjid'].queryset = Info.objects.filter(user=masjid_id)
print(masjid_id)
except Info.DoesNotExist:
### there is not userextend corresponding to this user, do what you want
pass
class Meta:
model = Imam
exclude = ('updated_at', 'created_at', 'user')
views.ppy
class NewImam(CreateView):
# template_name = "addimam.html"
# model = Imam
# form_class = ImamForm
# #success_url = reverse_lazy('person_changelist')
# def form_valid(self, form):
# #if not UserProfile.objects.filter(recruiter=self.request.user).exists():
# p = form.save(commit=False)
# p.user = self.request.user
# p.save()
# messages.success(self.request, 'The Imam Details Has Been Added Successully!')
# return redirect('addimam')
# # else:
# # messages.warning(self.request, 'The Profile has been Added Before!')
# # return redirect('recruiter:edit_recruiter_profile')
def get(self, request, *args, **kwargs):
form = ImamForm(user=self.request.user)
context = {'form': form}
return render(request, 'addimam.html', context)
def post(self, request, *args, **kwargs):
user = self.request.user
print(user)
user = CustomUser.objects.get(email=user)
masjid = user.id
print(type(masjid))
form = ImamForm(request.POST, instance=masjid)
print(form)
if form.is_valid():
p = form.save(commit=False)
p.user = self.request.user
p.save()
messages.success(self.request, 'The Project Details Has Been Added Successully!')
return redirect('addimam')
return render(request, 'addimam.html', {'form': form})
models.py
class Imam(models.Model):
CERTIFICATE = (
('PhD', 'PhD'),
('MSc', 'MSc'),
('Bsc', 'Bsc'),
)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
masjid = models.ForeignKey(Info, related_name='imam', on_delete=models.CASCADE)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
imam_address = models.TextField(blank=False, null=False)
phone_no = models.CharField(max_length=20)
certificate = models.CharField(max_length=50, choices=CERTIFICATE)
occupation = models.CharField(max_length=50, blank=False, null=False)
no_of_wives = models.IntegerField(blank=False, null=False)
no_of_children = models.IntegerField(blank=False, null=False)
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
Since you have override the __init__(...) method, Django expects the arguments in the different order.
So change
form = ImamForm(request.POST, instance=masjid)
to
form = ImamForm(masjid,request.POST)

Django form __init__ doesn't work in userprofileform

I'm stuck with the initialization of my userprofileform in the post method of a CBV and the instance receive a None object from the call to super()...
the model is quite standard : a user and his profile; the uerprofile model has an user attribute (onetoone)
models.py
[...]
class User(AbstractUser):
"""User model."""
username = None
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), unique=False, max_length=100)
last_name = models.CharField(_('last name'), unique=False, max_length=100)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone = models.CharField(max_length=15,null=True)
website = models.URLField(blank=True, null=True) upload_to=PathAndRename("avatars/"))
photo = models.ForeignKey(Photo, null=True, on_delete=models.PROTECT)
organization = models.CharField(_('organization'), null=True,unique=False, max_length=100)
city = models.CharField(_('city'), unique=False, max_length=100, null=True)
country = CountryField(blank_label=_('select country'), null=True)
subscribe_newsletter = models.BooleanField(default=False)
language = models.CharField(max_length=10,
choices=settings.LANGUAGES,
default=settings.LANGUAGE_CODE)
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
def save(self, *args,**kwargs):
#import ipdb; ipdb.set_trace()
self.city=(self.city or '').lower()
self.organization=(self.organization or '').lower()
self.website=(self.website or '').lower()
super(UserProfile, self).save(*args,**kwargs)
self.user.save()
[...]
forms.py
[...]
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('first_name', 'last_name', 'phone', 'website','city','country','organization','subscribe_newsletter')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user',None)
super(UserProfileForm, self).__init__(*args, **kwargs)
try:
self.instance.user=user
except:
pass
try:
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
except User.DoesNotExist:
pass
[...]
views.py
class ProfileView(LoginRequiredMixin, View):
def __init__(self,*args,**kwargs):
self.context={}
super().__init__()
def get(self,request):
profile_form = UserProfileForm(instance=request.user.profile, prefix='profile')
self.context['profile_form']=profile_form
return render(request,'account/profile.html', self.context)
def post(self,request):
#import ipdb; ipdb.set_trace()
if request.POST.get('action',)=='profile':
profile_form = UserProfileForm(request.POST, user=request.user, prefix='profile')
if profile_form.is_valid():
profile_form.save()
info={'title':_('PROFILE SAVED !'),'desc':_('your profile has been successfully updated')}
self.context['infos']=[info]
return render(request,'info.html', self.context)
else:
self.context['errors']=get_form_errors_list(profile_form.errors)
elif request.POST.get('action',)=='avatar':
pass
self.context['profile_form']=profile_form
return render(request,'account/profile.html', self.context)
[...]
The error occurs in the post method of the view when I affect UserProfileForm to profile_form. The debug toolbar return "UserProfile has no user".
The init method of UserProfileForm works with the get method of the view (the instance is set whith the user.profile) but the instance is not given while posting (throwing user.profile would be nonsense as the data are contained in the resquest.POST)...

User has no dcf_profile : RelatedObjectDoesNotExist

After extending an existing user model, I get RelatedObjectDoesNotExist exception with a value User has no dcf_profile. I seems that dcf_profile isn't created automatically for each user.
Please take a look at my model, view and form below and tell me how can I correct my views file?
My models.py :
class CustomUser(models.Model):
auth_user_ptr = models.OneToOneField(
User,
parent_link=True,
related_name='dcf_profile',
primary_key=True
)
phone = models.CharField(_('phone'), max_length=30, null=True, blank=True)
receive_news = models.BooleanField(_('receive news'), default=True, db_index=True)
class Meta:
app_label = 'dcf'
def allow_add_item(self):
if self.item_set.count() > settings.DCF_ITEM_PER_USER_LIMIT:
return False
else:
return True
class Item(models.Model):
slug = models.SlugField(blank=True, null=True, max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, verbose_name=_('group'))
title = models.CharField(_('title'), max_length=100)
description = models.TextField(_('description'))
price = models.DecimalField(_('price'), max_digits=10, decimal_places=2)
phone = models.CharField(_('phone'), max_length=30)
is_active = models.BooleanField(_('display'), default=True, db_index=True)
updated = models.DateTimeField(_('updated'), auto_now=True, db_index=True)
posted = models.DateTimeField(_('posted'), auto_now_add=True)
def __unicode__(self):
return self.title
class Meta:
verbose_name = _('item')
verbose_name_plural = _('items')
ordering = ('-updated', )
def get_absolute_url(self):
return reverse('item', kwargs={
'pk': self.pk,
'slug': self.slug
})
def get_title(self):
return u'%s' % self.title
def get_description(self):
return u'%s' % self.description[:155]
def get_keywords(self):
# TODO need more optimal keywords selection
return ",".join(set(self.description.split()))
def get_related(self):
# TODO Need more complicated related select
return Item.objects.exclude(pk=self.pk)[:settings.DCF_RELATED_LIMIT]
def save(self, *args, **kwargs):
if self.slug is None:
self.slug = slugify(unidecode(self.title))
super(Item, self).save(*args, **kwargs)
My views.py :
class ItemCreateView(FormsetMixin, CreateView):
is_update_view = False
model = Item
form_class = ItemCreateEditForm
formset_class = inlineformset_factory(Item, Image, extra=3, fields=('file', ))
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
if not self.request.user.dcf_profile.allow_add_item():
messages.error(self.request, _('You have reached the limit!'))
return redirect(reverse('my'))
return super(ItemCreateView, self).dispatch(*args, **kwargs)
def form_valid(self, form, formset):
form.instance.user = self.request.user
form.save()
return super(ItemCreateView, self).form_valid(form, formset)
My forms.py
class ItemCreateEditForm(forms.ModelForm):
class Meta:
model = Item
fields = ('group', 'title', 'description', 'price', 'phone', 'is_active')
Just to share with you a solution that worked, I have created a proxy model of the User model and updated my views.py :
Updated models.py
class DcfUser(User):
class Meta:
proxy = True
def allow_add_item(self):
if self.item_set.count() > settings.DCF_ITEM_PER_USER_LIMIT:
return False
else:
return True
Updated views.py
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
user = self.request.user
my_user = DcfUser.objects.get(username=user)
if not my_user.allow_add_item():
messages.error(self.request, _('You have reached the limit!'))
return redirect(reverse('my'))
Thank you