User has no dcf_profile : RelatedObjectDoesNotExist - django

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

Related

How can I access object in Mixin via dispatch?

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

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 %}

Recursively going through a model

I have a model like so:
class CustomUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
slug = models.SlugField(blank=True)
def lastfolder(self):
try:
return self.folder_set.all().order_by('-created_at')[0]
except IndexError:
return None
def lastdrive(self):
try:
return self.drive_set.all().order_by('-created_at')[0]
except IndexError:
return None
def lastfile(self):
try:
return self.drive_set.all().order_by('-created_at')[0]
except IndexError:
return None
def save(self, *args, **kwargs):
self.slug = slugify(self.user.username)
return super().save(*args, **kwargs)
def get_absolute_url(self):
kwargs = {'slug' : self.slug}
return reverse('', kwargs=kwargs)
def __str__(self):
return self.user.username
class Drive(models.Model):
owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
cover_picture = models.ImageField(upload_to=f'media/{user_path}', default="media/drive.png")
created_at = models.DateTimeField()
slug = models.SlugField(blank=True)
def get_absolute_url(self):
kwargs = {'slug' : self.slug}
return reverse('', kwargs=kwargs)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
return super().save(*args, **kwargs)
class Meta:
unique_together = [['owner', 'name']]
ordering = ('name',)
class Folder(models.Model):
owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
drive = models.ForeignKey('Drive', on_delete=models.CASCADE)
name = models.CharField(max_length=64)
parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True)
cover_picture = models.ImageField(upload_to=f'media/{user_path}', default="media/folder-white.png")
created_at = models.DateTimeField()
path = models.CharField(max_length=2048, blank=True)
slug = models.SlugField(blank=True)
def __str__(self):
return self.name
def get_absolute_url(self):
kwargs = {'slug' : self.slug}
return reverse('', kwargs=kwargs)
def get_path(self):
yield self.name
try:
yield from get_path(self.parent)
except:
pass
def complete_get_path(self):
text = []
for i in self.get_path():
text.append(i)
text.reverse()
return "/".join(text)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
self.path = self.complete_get_path()
return super().save(*args, **kwargs)
class Meta:
unique_together = [['parent', 'name']]
ordering = ('name',)
If I create a Folder instance with a name Programming and another Folder instance with a name Python and parent with reference to Programming, it is supposed to populate the path field for Programming with "Programming" and Python with "Programming/Python". Unfortunately, it does not work
My emphasis is on the Folder model and the methods: get_path, complete_get_path, and save.
After saving the model, and checking the path field in the admin, it just shows only the name attribute not return the actual generated path of the folder.
How do I go about it.
The problem is that get_path isn't defined and the blanket try/except is eating your error. Your method should be:
def get_path(self):
yield self.name
yield from self.parent.get_path()
Here's a slight optimization. If you yield the parent's path's first, you don't have to reverse the list of folder names and then can simply join it at the end.
def get_path(self):
yield from self.parent.get_path()
yield self.name
def complete_get_path(self):
return "/".join(self.get_path())

Not able to created nested comment functionality using django rest framework

I am trying to make a nested comment functionality using django rest framework.
I am using Django 2.2 and rest framework version 3.10.2 but here i am following a video in youtube that uses Django 1.9.
No matter what value i passed in URL, it always return a validation error stating that
model_qs is empty.
I am not able to solve this issue. Can someone please look into it and
let me point out what i am doing wrong here.
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
urls.py
urlpatterns = [
path('create/', CommentCreateAPIView.as_view(), name='create'),
]
model.py
class CommentManager(models.Manager):
def all(self):
qs = super(CommentManager, self).filter(parent=None)
return qs
def filter_by_instance(self, instance):
content_type = ContentType.objects.get_for_model(instance.__class__)
obj_id = instance.id
qs = super(CommentManager, self).filter(content_type=content_type, object_id=obj_id).filter(parent=None)
return qs
def create_by_model_type(self, model_type, slug, content, user, parent_obj=None):
model_qs = ContentType.objects.filter(model=model_type)
if model_qs.exists():
some_model = model_qs.first().model_class()
obj_qs = some_model.objects.filter(slug=slug)
if obj_qs.exists() and obj_qs.count() == 1:
instance = self.model()
instance.content = content
instance.user = user
instance.content_type = model_qs.first()
instance.object_id = obj_qs.first().id
if parent_obj:
instance.parent = parent_obj
instance.save()
return instance
return None
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
objects = CommentManager()
class Meta:
ordering = ['-timestamp']
def __unicode__(self):
return str(self.user.username)
def __str__(self):
return str(self.user.username)
def get_absolute_url(self):
return reverse("comments:thread", kwargs={"id": self.id})
def get_delete_url(self):
return reverse("comments:delete", kwargs={"id": self.id})
def children(self): # Replies
return Comment.objects.filter(parent=self)
#property
def is_parent(self):
if self.parent is not None:
return False
return True
serializers.py
def create_comment_serializer(model_type='post', slug=None, parent_id=None, user=None):
class CommentCreateSerializer(ModelSerializer):
class Meta:
model = Comment
fields = [
'id',
'parent',
'content',
'timestamp',
]
def __init__(self, *args, **kwargs):
self.model_type = model_type
self.slug = slug
self.parent_obj = None
if parent_id:
parent_qs = Comment.objects.filter(id=parent_id)
if parent_qs.exists() and parent_qs.count() == 1:
self.parent_obj = parent_qs.first()
return super(CommentCreateSerializer, self).__init__(*args, **kwargs)
def validate(self, data):
model_type = self.model_type
model_qs = ContentType.objects.filter(model=model_type)
print(model_qs) # This always return <QuerySet []>
if not model_qs.exists() or model_qs.count() != 1:
raise ValidationError("This is not a valid content types")
some_model = model_qs.first().model_class()
obj_qs = some_model.objects.filter(slug=self.slug)
if not obj_qs.exists() or obj_qs.count() != 1:
raise ValidationError("This is not a valid slug")
return data
def create(self, validated_data):
content = validated_data.get("content")
if user:
main_user = user
else:
main_user = User.objects.all().first()
model_type = self.model_type
slug = self.slug
parent_obj = self.parent_obj
comment = Comment.objects.create_by_model_type(model_type, slug, content, main_user, parent_obj=parent_obj,)
return comment
return CommentCreateSerializer
views.py
class CommentCreateAPIView(CreateAPIView):
queryset = Comment.objects.all()
# permission_classes = [IsAuthenticated]
def get_serializer_class(self):
model_type = self.request.GET.get("type")
slug = self.request.GET.get("slug")
parent_id = self.request.GET.get("parent_id", None)
return create_comment_serializer(
model_type=model_type,
slug=slug,
parent_id=parent_id,
user=self.request.user
)

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)...