Recursively going through a model - django

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

Related

Generic UpdateView isn't working with slug

I'm pretty new to django.I am trying to update a post with generic UpdateView. But the post isn't updating after filling up the form.Im accessing the update view through slug url.
My model:
class Post(models.Model):
title = models.CharField(max_length=60)
post_body = models.TextField()
time_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE)
slug = models.SlugField(null=False,unique=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('postdetail', kwargs={'slug': self.slug})
def save(self,*args,**kwargs):
if not self.slug:
author_id = str(self.author.id)
self.slug = slugify(self.title +'-'+author_id)
return super().save(*args, **kwargs)
My view:
class postupdate(LoginRequiredMixin,UserPassesTestMixin,UpdateView):
model = Post
fields = ['title','post_body']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
else:
return False
My url:
path('post/<slug:slug>/updatepost/', postupdate.as_view(),name = 'updatepost'),
It seems the save() method is not getting called every time.
class Post(models.Model):
# rest of your code
def save(self, *args, **kwargs):
if not self.slug:
author_id = str(self.author.id)
self.slug = slugify(self.title + '-' + author_id)
return super().save(*args, **kwargs) # outside the `if...` clause

get() returned more than one Post : return 2

i was trying to make like button for my django blog but i'm getting error while hitting like the error is get() returned more than one Post -- it returned 2!
here is my code
views.py
class PostLikeRedirect(RedirectView):
def get_redirect_url(self, *args, **kwargs):
obj = get_object_or_404(Post)
url_ = obj.get_absolute_url()
user = self.request.user
if user.is_authenticated():
obj.likes.add(user)
return url_
models.py
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(User,on_delete=models.CASCADE)
likes = models.ManyToManyField(User, blank=True,related_name='post_likes')
content = models.TextField()
img = models.ImageField(upload_to='pics',blank=True)
time = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('LoveTravel-Details', kwargs={'pk': self.pk})
urls.py
path('blog/<int:pk>/like/', PostLikeRedirect.as_view(),name='Like'),
This line isn't filtering the posts, so it returns all of them
obj = get_object_or_404(Post)
You want to filter by the primary key, which you can fetch from **kwargs
def get_redirect_url(self, *args, **kwargs):
obj = get_object_or_404(Post, pk=kwargs['pk'])
...

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

django forms and __init__ function: unexpected keyword argument 'user'

I follow kenneth love crash course and I get unexpected error which I can not solve:
"init() got an unexpected keyword argument 'user'"
here is the form class:
class TalkTalkListForm(forms.ModelForm):
class Meta:
model = models.Talk
fields = ('talk_list',)
def __init__(self, *args, **kwargs):
super(TalkTalkListForm, self).__init__(*args, **kwargs)
self.fields['talk_list'].queryset = (
self.instance.talk_list.user.lists.all())
self.helper = FormHelper()
self.helper.layout = Layout(
'talk_list',
ButtonHolder(
Submit('move', 'Move', css_class='btn-primary')
)
)
and here is Talk model:
class Talk(models.Model):
ROOM_CHOICES = (
('BS1_O', 'BS1_O'),
('BS1TC', 'BS1TC'),
('BS3_O', 'BS3_O'),
('BS3TC', 'BS3TC'),
)
talk_list = models.ForeignKey(TalkList, related_name='talks')
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, blank=True)
when = models.DateTimeField()
room = models.CharField(max_length=5, choices=ROOM_CHOICES)
host = models.CharField(max_length=255)
talk_rating = models.IntegerField(blank=True, default=0)
speaker_rating = models.IntegerField(blank=True, default=0)
notes = models.TextField(blank=True, default='')
notes_html = models.TextField(blank=True, default='', editable=False)
class Meta:
ordering = ('when', 'room')
unique_together = ('talk_list', 'name')
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
self.notes_html = mistune.markdown(self.notes)
super(Talk, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('talks:talks:detail', kwargs={'slug': self.slug})
#property
def overall_rating(self):
if self.talk_rating and self.speaker_rating:
return (self.talk_rating + self.speaker_rating) / 2
return 0
I can see Talk has not got an attribute 'user' but I pass 'user' attribute to init somehow. Is it the reason of the problem ?
I know kwargs are named arguments so a named argument not expected will throw an error but I do not know how to get rid off the error.
UPDATE:
class TalkDetailView(views.LoginRequiredMixin, generic.DetailView):
http_method_names = ['get', 'post']
model = models.Talk
def get_queryset(self):
return self.model.objects.filter(talk_list__user=self.request.user)
def get_context_data(self, **kwargs):
context = super(TalkDetailView, self).get_context_data(**kwargs)
obj = context['object']
rating_form = forms.TalkRatingForm(self.request.POST or None,instance=obj)
list_form = forms.TalkTalkListForm(self.request.POST or None,instance=obj)
context.update({
'rating_form': rating_form,
'list_form': list_form
})
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
if 'save' in request.POST:
talk_form = forms.TalkRatingForm(request.POST or None,
instance=self.object)
if talk_form.is_valid():
talk_form.save()
if 'move' in request.POST:
list_form = forms.TalkTalkListForm(request.POST or None,
instance=self.object,
user=request.user)
if list_form.is_valid():
list_form.save()
return redirect(self.object)
I don't understand why do you put request.user in init if you have already filtered queryset by request.user == return self.model.objects.filter(talk_list__user=self.request.user)
Anyway ...
1) If you want access kwarg in form __init__ you should pop it before call super...
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', False)
# now you have removed it from kwargs and `super` call will work as expected
super(TalkTalkListForm, self).__init__(*args, **kwargs)
# do smth with `user`
2) If you want save request.user to object do smth like this in your view:
if list_form.is_valid():
obj = list_form.save()
obj.talk_list.user = request.user
obj.save()

optimizing django code model

I want to optimizing this code. I think the best solution is to use the method pre_save and not override the save method. This is the function that delete the old image when in editing it upload new image
def delete_old_image(sender, instance):
try:
obj = sender.objects.get(id=instance.id)
except sender.DoesNotExist:
pass
else:
if not obj.image == instance.image:
try:
os.remove(obj.image.path)
except:
pass
under the code of the model
class Service(models.Model):
title= models.CharField(max_length=170)
slug = models.SlugField(max_length=200, blank=True, unique=True, editable=False)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Service, self).save(*args, **kwargs)
class Portfoglio(models.Model):
title= models.CharField(max_length=170, unique=True)
slug = models.SlugField(max_length=200, blank=True, unique=True, editable=False)
image=models.ImageField(upload_to = 'images/' , default= 'images/foto.jpg', verbose_name='upload')
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
if self.id is not None:
delete_old_image(Portfoglio, self)
super(Portfoglio, self).save(*args, **kwargs)
class Image(models.Model):
title= models.CharField(max_length=200)
image=models.ImageField(upload_to = 'images/' , default= 'images/foto.jpg', verbose_name='upload')
def save(self, *args, **kwargs):
if self.id is not None:
delete_old_image(Portfoglio, self)
super(Image, self).save(*args, **kwargs)
class Team(models.Model):
name= models.CharField(max_length=200)
image= models.ImageField(upload_to = 'images/' , default= 'images/foto.jpg', verbose_name='upload')
def save(self, *args, **kwargs):
if self.id is not None:
delete_old_image(Team, self)
super(Team, self).save(*args, **kwargs)
from django.db.models.signals import pre_save
from django.dispatch import receiver
#receiver(pre_save)
def pre_delete_old_image(sender, instance, created, **kwargs):
if sender not in [Service, Portfoglio, Image, Team]:
return
if getattr(sender, 'slug', False):
instance.slug = slugify(instance.title)
if not created and getattr(sender, 'image', False):
delete_old_image(sender, instance)
def delete_old_image(sender, instance):
try:
obj = sender.objects.get(id=instance.id)
except sender.DoesNotExist:
pass
else:
if obj.image != instance.image:
try:
os.remove(obj.image.path)
except Exception as e:
pass