How to properly remove a specific ManyToMany relationship? - python

I have a ManyToMany relationship with one of my Models. On deleting a child, I want to remove the relationship but leave the record as it might be being used by other objects. On calling the delete view, I get an AttributeError error:
Exception Value: 'QuerySet' object has
no attribute 'clear'
This is my models.py:
class Feed(models.Model):
username = models.CharField(max_length=255, unique=True)
class Digest(models.Model):
name = models.CharField(max_length=255)
user = models.ForeignKey(User)
items = models.PositiveIntegerField()
keywords = models.CharField(max_length=255, null=True, blank=True)
digest_id = models.CharField(max_length=20, unique=True)
time_added = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=1)
feeds = models.ManyToManyField(Feed)
And the relevant section of views.py:
def feed_delete(request, id):
digest = get_object_or_404(Digest, id=id)
if digest.user == request.user:
Feed.objects.get(id=request.POST.get('id')).digest_set.filter(id=id).clear()
return HttpResponseRedirect(digest.get_absolute_url())

Clear the fields on a Digest isntance
digest = get_object_or_404(Digest, id=id)
if digest.user == request.user:
digest.feeds.clear()
#do your processing
In response to your comment.
digest = get_object_or_404(Digest, id=id)
if digest.user == request.user:
feed=digest.feeds.get(id=2)#get an instance of the feed to remove
digest.feeds.remove(feed)#remove the instance
Hope this helps!

Related

Sorting (order_by) in list

I am building a Blog App and I am trying to sort or order_by in list which contains multiple queries.
models.py
class BlogPost(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=30)
date = models.DateTimeField(auto_now_add=True)
class Comments(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
blog_of = models.ForeignKey(BlogPost, on_delete=models.CASCADE)
body = models.CharField(max_length=30, default='')
date_added = models.DateTimeField(auto_now_add=True)
views.py
def mypage(request):
query_1 = list(BlogPost.objects.filter(user=request.user).order_by('-date'))
query_2 = list(Comment.objects.filter(user=request.user).order_by('date_added'))
results = sorted(chain(query_1, query_2),key=attrgetter('date') , reverse=True)
context = {'results':results}
return render(reques, 'mypage.html', context)
But is showing
'Comment' object has no attribute 'date'
And I think this is because date field name is different in both model and i am sorting with only one, But i have no idea how can I sort with different field name.
Any help would be much Appreciated. Thank You
Or just add it as a property:
class Comments(models.Model): # do NOT give a model a plural name!
# ....
#property
def date(self):
return self.date_added
# or if it is a datetimefield
# return self.date_added.date()
ALso you can just write a more customized sorting key (e.g. in case the involved models are from third-party apps).
def order(obj):
try:
return obj.date
except AttributeError:
return obj.date_added
# ...
results = sorted(chain(query_1, query_2), key=order, reverse=True)

How to pass object assignment to a foreign key relationship in REST Framework?

I have two models:
class Group(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
class User(models.Model):
username = models.CharField(max_length=50, null=False, blank=False)
password = models.CharField(max_length=50, null=False, blank=False)
group = models.ForeignKey(Group, null=False, on_delete=models.CASCADE)
And I have serializers for both of them:
class GroupSerializer(serializers.Serializer):
name = serializers.CharField()
class UserSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
group = serializers.PrimaryKeyRelatedField(queryset=Group.objects.all())
Then in the registration view, I'm trying to automatically create a group when user is registering their account:
#api_view(['POST'])
def registration(request):
# Creates a new group
g_serializer = GroupSerializer(data=request.data, partial=True)
g_serializer.is_valid(raise_exception=True)
group = g_serializer.save()
# Creates a new user
u_serializer = UserSerializer(data=request.data, partial=True)
u_serializer.is_valid(raise_exception=True)
user = u_serializer.save()
As you can imagine the error I'm getting is that group field for User class violates not null restriction.
I tried to fix it by doing u_serializer['group'] = group.id or u_serializer['group'] = group but it throws the following error:
TypeError: 'UserSerializer' object does not support item assignment
How can I handle this? The new Group object is being properly created and inserted into the db and I need to assign that object to user.group.
According to this https://stackoverflow.com/a/44718400/3734484 in your case would be:
...
# Creates a new user
u_serializer = UserSerializer(data=data, partial=True)
u_serializer.is_valid(raise_exception=True)
user = u_serializer.save(group=group)
...

How to get the other fields of a ForeignKey in Django

I have two models as Author and Comment
One is Comment and it has a ForeinKey points to Author(BlogUser)
class Comment(models.Model):
body = models.TextField('body', max_length=500)
created_time = models.DateTimeField('create_time', default=now)
last_mod_time = models.DateTimeField('last_mod_time', default=now)
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='author', on_delete=models.CASCADE)
article = models.ForeignKey(Article, verbose_name='article', on_delete=models.CASCADE)
parent_comment = models.ForeignKey('self', verbose_name="parent_comment", blank=True, null=True, on_delete=models.CASCADE)
is_approved = models.BooleanField('is_approved', default=True, blank=False, null=False)
is_enabled = models.BooleanField('is_enabled', default=True, blank=False, null=False)
is_deleted = models.BooleanField('is_deleted', default=True, blank=False, null=False)
Author Model
class BlogUser(AbstractUser):
qq_number = models.CharField('QQ', max_length=20, default='')
I am trying to get all the comments related to a specified Article and display the comments list by the format of usernames and comment bodys.
In the view I filter all the comments by the article id, but after serializing the filter queryset, it gives me only the Author's primary id, how could I get the other feilds of Author? Shall I write a function by myself or there is some easier way to do it?
The view:
def get_comment_list(request, article_id):
if request.GET:
comment_list = models.Comment.objects.filter(article_id = article_id)
data = serializers.serialize('json',comment_list)
return HttpResponse(data, content_type="application/json")
else:
PermissionDenied('Not accept post')
You need to iterate the comment_list and with the member operator to access the fields of author
eg: comment_list[0].author.field_name
NOTE: Field name could be any field name that has been decleared in AUTH_USER_MODEL table like first_name, last_name or username.
By default, django serializer does not provide information from ForeignKey. I would also recommend using Django Rest Framework Serializer(Denial Roseman mentioned in the comments) if you have the scope of using it.
Still you can use the django serializer, but you will need to override the json.Serializer's end_object() method. There you canget the values of the user object like following example:
from django.core.serializers.json import Serializer
class CustomSerializer(Serializer):
def end_object(self, obj):
for field in self.selected_fields:
if field in self._current.keys():
continue
else:
try:
self._current[field] = getattr(obj.author, field) # Fetching the data from ForeignKey
except AttributeError:
pass
super(CustomSerializer, self).end_object(obj)
# usage
data = CustomSerializer().serialize(
Comment.objects.all(),
fields = (
'pk',
# other fields in Comment Object
'username' # it belongs to Author Model
)
Downside of this solution is that, if BlogUser has same field as Comment, then it will fetch the value of Comment.
More Advanced Implementation:
class CustomSerializer(Serializer):
def end_object(self, obj):
for field in self.selected_fields:
if field in self._current.keys():
continue
else:
if field.find('__') > -1:
try:
fks = field.split('__')
curr_value = obj
while(len(fks)>0):
current_key = fks.pop(0)
curr_value = getattr(curr_value, current_key)
self._current[field] = curr_value
except AttributeError:
pass
super(CustomSerializer, self).end_object(obj)
# usage
data = CustomSerializer().serialize(
Comment.objects.all(),
fields = (
'pk',
# other fields in Comment Object
'author__username' # it belongs to Author Model
)

Tastypie Attributes & Related Names, Empty Attribute Error

I'm getting this error:
The object '' has an empty attribute 'posts' and doesn't allow a default or null value.
I'm trying to get the number of 'votes' on a post and return it in my models.py:
class UserPost(models.Model):
user = models.OneToOneField(User, related_name='posts')
date_created = models.DateTimeField(auto_now_add=True, blank=False)
text = models.CharField(max_length=255, blank=True)
def get_votes(self):
return Vote.objects.filter(object_id = self.id)
Here's my resource:
class ViewPostResource(ModelResource):
user = fields.ForeignKey(UserResource,'user',full=True)
votes= fields.CharField(attribute='posts__get_votes')
class Meta:
queryset = UserPost.objects.all()
resource_name = 'posts'
authorization = Authorization()
filtering = {
'id' : ALL,
}
What am I doing wrong?
The attribute value that you have defined isn't proper.
You can achieve what you want in a few ways.
Define a dehydrate method:
def dehydrate(self, bundle):
bundle.data['custom_field'] = bundle.obj.get_votes()
return bundle
Or set the get_votes as property and define the field in resource like so (I recommend this one as it is the most clear):
votes = fields.CharField(attribute='get_votes', readonly=True, null=True)
Or define it this way:
votes = fields.CharField(readonly=True, null=True)
And in the resources define the dehydrate_votes method like so:
def dehydrate_votes(self, bundle):
return bundle.obj.get_votes()

Confused about how is_valid works

I have a view which validates data from a form which just has some basic information about an item. I'm confused with how the is_valid method works here even after reading
this . If the user doesn't input some of the required fields like name or image 1, I want them to see the error on the page "this field is required" or something of that nature. I thought if the form.is_valid returned False, these messages would automatically be raised on the page for the user. Or do I need to specify what error message for each field somewhere that I would want the user see?
#view
def sell(request):
if request.method == "POST":
form = AddItem(request.POST, request.FILES)
if form.is_valid():
item = form.save(commit=False)
item.user = request.user
item.is_active = True
item.slug = slugify(item.name)
item.save()
return HttpResponseRedirect('thanks.html')
else:
form = AddItem()
return render_to_response('forsale.html', locals(), context_instance=RequestContext(request))
#form
class AddItem(forms.ModelForm):
name = forms.CharField(label="Title")
class Meta:
model = Item
exclude = ('user','slug','is_active',)
#model
class Item(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=75)
slug = models.SlugField(max_length=50, unique=True)
is_active = models.BooleanField(default=True, blank=True)
image1 = models.ImageField(upload_to='img')
image2 = models.ImageField(upload_to='img', blank=True)
image3 = models.ImageField(upload_to='img', blank=True)
image_caption1 = models.CharField(max_length=200, blank=True)
image_caption2 = models.CharField(max_length=200, blank=True)
image_caption3 = models.CharField(max_length=200, blank=True)
price = models.DecimalField(max_digits=8, decimal_places=2)
quantity = models.IntegerField(default=1)
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
shipping_price = models.DecimalField(decimal_places=2, max_digits=6)
categories = models.ManyToManyField(Category)
You need to extract the errors from the form object using form.errors then deal with the dict however you want. If you're using ajax, simply send the dict as json back over and use javascript to handle it. If it was a direct html form submit, then you need to render and respond with a page with the errors in the passed dictionary and deal with the passed error in the template (usually with an {% if errors %} tag

Categories

Resources