Is it possible to create status choice with a model? - django

I have my model.py:
class Example(models.Model):
STATUS_CHOICE = (
('status1', 'status1'),
('status2', 'status2'),
)
field= models.CharField(max_length=10, choices=STATUS_CHOICE)
.
But I want to know if it's possible to have the possibility to create my STATUS_CHOICE with a model, in this manner the client can create the status as you want.

It certainly is. It would be a ForeignKey to a Status model.
class Status(models.Model):
slug = models.SlugField(unique=True)
title = models.TextField()
class Example(models.Model):
status = models.ForeignKey(Status)
You may want to have a migration create initial statuses, unless all of them should be user-defined.
You will probably want to use a SlugRelatedField (or whatsitcalled) in DRF, so users can just post {"status": "foo"}, not {"status": 1} or whatever happens to be the foo status's primary key.

Related

Check if item exists in database by field value

I have a class called Product and I want to check if there is already a product in the database with the same title.
class Product(models.Model):
title = models.CharField(max_length=255)
...
for (something in something):
check if db contains product with a title of 'Some Product'
You can work with .exists() [Django-doc]:
Product.objects.filter(title='Some Product').exists()
It might however be better to enforce uniqness at the database level, with unique=True [Django-doc]:
class Product(models.Model):
title = models.CharField(max_length=255, unique=True)
then if the database enforces (most databases do), it will simply be impossible to create a new Product with the same title.
You can also obtain a Product, or create it if it does not yet exists, with .get_or_create(…) [Django-doc]:
my_prod, created = Product.objects.get_or_create(title='Some Product')

Which pattern to use for a model to have different variations of? Generic FK, multi-table, inheritance, others?

I am having trouble deciding how to structure my models for a particular data structure.
The models I have would be Posts, Groups, Users.
I want the Post model that can be posted from a groups page or user page and potentially more, like an events page.
Posts would contain fields for text, images(fk), user, view count, rating score (from -- a reference to where ever it was posted from like user or group page, though I am unsure how to make this connection yet)
I thought about using a Generic Foreign Key to assign a field to different models but read articles suggesting to avoid it. I tried the suggested models, but I wasn't unsure if they were the right approach for what I required.
At the moment I went with Alternative 4 - multi-table inheritance
class Group(models.Model):
name = models.CharField(max_length=64)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='_groups')
members = models.ManyToManyField(
settings.AUTH_USER_MODEL)
def __str__(self):
return f'{self.name} -- {self.created_by}'
def save(self, *args, **kwargs):
# https://stackoverflow.com/a/35647389/1294405
created = self._state.adding
super(Group, self).save(*args, **kwargs)
if created:
if not self.members.filter(pk=self.created_by.pk).exists():
self.members.add(self.created_by)
class Post(models.Model):
content = models.TextField(blank=True, default='')
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="%(app_label)s_%(class)s_posts",
related_query_name="%(app_label)s_%(class)ss")
# class Meta:
# abstract = True
def __str__(self):
return f'{self.content} -- {self.created_by}'
class PostImage(models.Model):
image = models.ImageField(upload_to=unique_upload)
post = models.ForeignKey(
Post, related_name='images', on_delete=models.CASCADE)
def __str__(self):
return '{}'.format(self.image.name)
class UserPost(models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_uPost', on_delete=models.CASCADE)
class GroupPost(models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_gPost', on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
To do some specific filters ex:
Filter specific group post
Post.objects.filter(_gPost__group=group)
Filter specific user post
Post.objects.filter(created_by=user) # exclude groups with _gPost__isnull=False
Create post to user/group
p = Post.objects.create(...)
up = UserPost.objects.create(post=p)
gp = GroupPost.objects.create(post=p)
Really I am wondering if this is a sensible approach. The current way of a filter and creating feel odd. So only thing making me hesitant on this approach is just how it looks.
So, is Generic ForeignKey the place to use here or the current multi-table approach. I tried going with inheritance with abstract = True and that was unable to work as I need a foreign key to base post model. Even with no abstract, I got the foreign key reference, but filter became frustrating.
Edit:
So far only weird issues(but not really) are when filtering I have to be explicit to exclude some field to get what I want, using only .filter(created_by=...) only would get all other intermediate tables.
Filter post excluding all other tablets would requirePost.objects.filter(_uPost__isnull=True, _gPost__isnull=True, _**__isnull=True) which could end up being tedious.
I think your approach is sensible and that's probably how I would structure it.
Another approach would be to move the Group and Event foreignkeys into the Post model and let them be NULL/None if the Post wasn't posted to a group or event. That improves performance a bit and makes the filters a bit more sensible, but I would avoid that approach if you think Posts can be added to many other models in the future (as you'd have to keep adding more and more foreignkeys).
At the moment I will stick with my current pattern.
Some extra reading for anyone interested.
https://www.slideshare.net/billkarwin/sql-antipatterns-strike-back/32-Polymorphic_Associations_Of_course_some

ValueError: Cannot use Queryset for "": Use a Queryset for ""

I'm working on a little project using these models here and I'm trying to figure out a way to get a set of all the posts associated with users the currently authenticated user is following.
But I keep getting:
Cannot use QuerySet for "Profile": Use a QuerySet for "User".
class Profile(models.Model):
user = models.OneToOneField(User)
isInstructor = models.BooleanField(default=False)
isTutor = models.BooleanField(default=False)
isStudent = models.BooleanField(default=False)
isAdmin = models.BooleanField(default=False)
following = models.ManyToManyField('self', related_name = "followers", blank=True, symmetrical=False)
profile_image = ImageField(upload_to=get_image_path, blank=True, null=True)
class Post(models.Model):
title = models.CharField(max_length=100)
topic = models.CharField(max_length=50)
description = models.CharField(max_length=1200)
poster = models.ForeignKey(User, related_name="posts")
likes = models.IntegerField(default=0)
created = models.DateTimeField(auto_now_add=True)
tags = models.ManyToManyField(Tag, blank=True, related_name="posts")
def __str__(self):
return self.title
This is what keeps giving me the error.
current_user = Profile.objects.get(user = self.request.user)
Post.objects.filter(poster__in = current_user.following.all())
I searched around an found out that I had to use the __in operator whenever you want to filter by a list of things. But I keep getting the same error. Any help with explaining what the error means and what I can do to get around it would be much appreciated.
Maybe try something like this,
Post.objects.filter(poster__id__in=current_user.following.all().values_list('user_id'))
profile class is different to the user class. Therefore, the Profile instance is different to User's instance.
Instead of use current_user you need to use current_user.user.
You can check the documentation.
This is old, but I do not see a clear explanation of the error yet.
Consider this:
Post.poster is a foreign key to the User model.
current_user is a Profile object, not, as the name would suggest, a User object.
Profile.following is a m2m relation back to Profile, so it represents a Profile queryset.
Thus, when you filter on poster__in=current_user.following.all(), you're actually trying to compare a User with a Profile queryset.
This cannot be done, and Django is telling you exactly that:
Cannot use QuerySet for "Profile": Use a QuerySet for "User".
To fix this, you should provide a User queryset in the filter, e.g. something similar to zaidfazil's answer:
current_user_profile = Profile.objects.get(user=self.request.user)
Post.objects.filter(
poster__in=current_user_profile.following.values('user_id')
)
Or do something like this: https://stackoverflow.com/a/67247647
This does not answer the original post, but may help people who end up here based on the title:
A similar error message can also arise when your lookup refers to a reverse relation using '<fieldname>_set'.
For example, if a Bar model has a foreign key to a Foo model, then Foo will get a default related manager called Foo.bar_set. However, a lookup attempt like foo__bar_set__in=... would yield the following error:
ValueError: Cannot use QuerySet for "Bar": Use a QuerySet for "Foo".
This can be fixed by removing the _set from the lookup, so foo__bar_set__in=... should actually be foo__bar__in=....

Django Rest Framework Serializer charfield not updating when source is given

I have a model field with choices charfield
class Vehicle(models.Model):
name = models.CharField(max_length=100)
STATUS_CHOICES = (
("N", "New"),
("U", "Used"),
("P", "Just Purchased")
)
status = models.CharField(max_length=3, choices=STATUS_CHOICES)
The serializer class also has charfield for status but with source argument to display the readable value
class VehicleSerializer(ModelSerializer):
status = serializers.CharField(source='get_status_display')
class Meta:
model = Vehicle
When I try to update vehicles through patch request with data {'status': "U"}, there is no update performed.
However the update occurs when I remove the source from serializer status field.
Providing source is necessary to display proper value in web view.
I know the option of changing the name of status in serializer to something other and using that in the template. Also there is the option to override update method in serializer, however my question is what is source doing to prevent the update?
I think you need to add status to the fields list in meta.
class VehicleSerializer(ModelSerializer):
status = serializers.CharField(source='get_status_display')
class Meta:
model = Vehicle
fields = ('status',)

How do I use a ModelManager on a Django ManyToMany through field?

Let's say I have the following models:
class Poll(model):
title = models.CharField()
class Option(model):
title = models.CharField()
polls = models.ManyToManyField(
Poll,
through='PollOption',
null=True,
blank=True,
related_name='options'
)
class PollOptionManager(models.Manager):
use_for_related_fields = True
def get_queryset(self):
return super(PollOptionManager, self).get_queryset().filter(
is_active=True
)
class PollOption(model):
poll = ForeignKey(Poll)
option = ForeignKey(Option)
is_active = BooleanField(default=True)
objects = PollOptionManager()
When I try to query Poll.options.all() I'm still getting Option instances for which PollOption.is_active is False. How can I get my model manager to appropriately filter my ManyToMany relationship based on a flag on the through field?
The problem is that the through model's (related) manager is never actually used in your scenario. In order to utilize the custom manager, you have to explicitly use it, e.g.:
class Poll(models.Model):
#property
def active_options(self):
return Option.objects.filter(id__in=self.polloption_set.values_list('option'))
Here, polloption_set filters out inactive options as intended. This, however, makes the manager kind of pointless because you can just as well put the extra filter in the custom property.