Showing relationships inside the model - python

This is a model for my MCQ app. Is there a way to show relationship inside this particular model that answer belongs to one of the options and there should be only one right option
class Question(models.Model):
quiz_question=models.CharField(max_length=1000)
option1=models.CharField(max_length=500)
option2=models.CharField(max_length=500)
option3=models.CharField(max_length=500)
option4=models.CharField(max_length=500)
option5=models.CharField(max_length=500)
answer=models.CharField(max_length=500)
Thank You.

Always 5 Options
Add another field that points to the right option. For example, you could use a ChoiceField that has those choices:
(1, 'option1')
(2, 'option2')
(3, 'option3')
(4, 'option4')
(5, 'option5')
This solution is okay when there are always ever these 5 options.
Free amount of options
In this case, don't specify options as different fields but use ManyToManyFields and ForeignKeyFields:
class Option(Model):
text = TextField(unique=True)
class Question(Model):
quiz_question = TextField(null=False, blank=False)
answer = TextField()
options = ManyToManyField(Option)
selected_option = ForeignKeyField(Option)
Add a validator or clean_selected_option method that controls that the selected option is one of options.
Or use a through model that has an additional column selected:
class QuestionOption(Model):
option = ForeingKeyField(Option)
question = ForeignKeyField(Question)
selected = BooleanField(default=False)
def clean_selected(self):
# make sure only one option per question is selected
class Question(Model):
quiz_question = TextField(null=False, blank=False)
answer = TextField()
options = ManyToManyField(Option, through=QuestionOption)

Related

Setting field values for M2M object in djnago

I have two models in my application and I am trying to update a M2M field . Django documentation says that it can be done using set() method . But I guess by dealt set() accepts pk of the objects as arguments and what I want to do is set it using the actual values .
For e.g. I have a client in Clients models named "adam" and I want to set the value for its corresponding M2M field "items_onsale" to ["ac" , "book"]
Below is my code for the models :-
from django.db import models
class Client(models.Model):
SEX_CHOICES = [('M', 'Male'), ('F', 'Female')]
fname = models.CharField(max_length=100)
lname = models.CharField(max_length=100)
mailid = models.EmailField(max_length=100)
sex = models.CharField(max_length=1, choices=SEX_CHOICES, blank=True)
age = models.IntegerField()
items_onsale = models.ManyToManyField('Sinfo', blank=True)
def __str__(self): # for displaying the variable correctly.
return self.fname , self.lname , self.mailid , self.sex , self.age , self.items_onsale
class Sinfo(models.Model): # data of items put on sale by clients
iname = models.CharField(max_length=100)
idesc = models.TextField(max_length=300, null=True)
def __str__(self): # for displaying the variable correctly.
return self.iname
What I have tried till now is :-
c = Client.objects.get(pk=17)
list=["ac","book"]
c.items_onsale_set(list)
And I am getting below error :-
ValueError: Field 'id' expected a number but got 'book'
I know that there is a way to update it using values but not sure how . The django documentation does suggest using "through_defaults" option but haven't given any such example , so I am quite not sure how to use it :-
For many-to-many relationships set() accepts a list of either model instances or field values, normally primary keys, as the objs argument.
Use the through_defaults argument to specify values for the new intermediate model instance(s), if needed. You can use callables as values in the through_defaults dictionary and they will be evaluated once before creating any intermediate instance(s).
I am there is a better to achieve this , please let me know .
through_defaults option works if you have multiple foreign-keys in the same model when using the through option.
in your case, the items_onsale have a direct relationship with the client, use the add function instead of set.
c = Client.objects.get(pk=17)
list=["ac","book"]
c.items_onsale.set(list)
to
c = Client.objects.get(pk=17)
list= Sinfo.objects.filter(iname__in=["ac","book"])
c.items_onsale.add(*list)
add() accepts an arbitrary number of arguments, not a list of them.
To expand that list into arguments, use *

How to create multiple objects with different values at a time in django?

I need to create two models from a single template. Creating Product model is fine. The Product model has the ManyToOne relation with ProductVariant. But I got problem while creating ProductVariant model.
request.POST.getlist('names') this gives me the result like this ['name1','name2] and the same goes for all.
I want to create ProductVariant object with each values. How can I do this ? Also I think there is a problem while stroing a HStoreField. request.POST.getlist('attributes') gives the value like this ['a:b','x:z'] so I converted it into dictionary(but not sure it works).
UPDATE:
What I want is
attributes, names ... all will have the same number of items in the list.
For example if the name is ['a','b','c'] then weight will also have 3 values in the list [12,15,23] like this.
I want to create ProductVariant object 3 times since every list will have 3 items in the list. The first object will have field values from the list first item which is name=a,weight=12.. and for the second object values will be name=b, weight=15 like this.
How will it be possible? Or I should change the logic ? Any suggestions ?
models
class ProductVariant(models.Model):
name = models.CharField(max_length=255, blank=False, null=False)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
attributes = HStoreField()
price = models.FloatField(blank=False, null=False, default=0.0)
views
product = product_form.save()
attributes = request.POST.getlist('attributes')
names = request.POST.getlist('name')
up = request.POST.getlist('price')
weight = request.POST.getlist('weight')
print(names, 'names')
# converting attributes into the dictionary for the HStore field
for attribute in attributes:
attributes_dict = {}
key, value = attribute.split(':')
attributes_dict[key] = value
ProductVariant.objects.create(name=name,...) # for each value I want to create this.
Answer for update:
names = ['a', 'b', 'c']
weights = [12, 15, 23]
params = zip(names, weights)
products = [ProductVariant(name=param[0], weight=param[1]) for param in params]
ProductVariant.objects.bulk_create(products)
I disagree with this approach, but if you really want to do it this way, ziping would be the way as #forkcs pointed out.
I would use Django to help me as much as possible, before i get there, please make this change. float != money
class ProductVariant(models.Model):
name = models.CharField(max_length=255, blank=False, null=False)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
attributes = HStoreField()
price = models.DecimalField(blank=False, null=False, default=0, max_digits=6, decimal_places=2)
Once thats done, the form should look like this:
class ProductVariantForm(forms.ModelForm):
class Meta:
fields = ('name', 'product', 'attributes', 'price')
model = ProductVariant
ProductVariantFormSet = formset_factory(ProductVariantForm)
Note that I don't have to parse/clean/format attributes? Thats because Django did it for me ;)
And you can use it as follow IF you raname your fields and not use the same name multiple times: (instead of all your fields being called "attributes", you call them "form-X-attributes" where X is the number 0-infinity, example)
product = product_form.save()
formset = ProductVariantFormSet(data=request.POST)
if formset.is_valid():
instances = []
for form in formset:
if form.is_valid(): # this could probably be removed
instances.append(form.save())
For extra credit you can also do: (it shouldn't really matter)
product = product_form.save()
formset = ProductVariantFormSet(data=request.POST)
if formset.is_valid():
instances = []
for form in formset:
if form.is_valid(): # this could probably be removed
instances.append(form.save(save=False))
ProductVariant.objects.bulk_create(instances)
What do you gain? STANDARDS!!! AND compartmentalization! Everyone that knows Django knows what you did. All your clean logic will be placed in the right place (the form), and you'll be less error prone.
Ps. i wrote tests for you. https://gist.github.com/kingbuzzman/937a9d207bd937d1b2bb22249ae6bdb2#file-formset_example-py-L142
If you want more information on my approach, see the docs https://docs.djangoproject.com/en/3.1/topics/forms/formsets/
As for attributes, it could be reduced to one line like this:
attributes_dict = dict(map(lambda x: x.split(':'), attributes))
To create multiple objects you should either iterate and create one object at a time or use bulk_create:
for name in names:
ProductVariant.objects.create(name=name,...)
Or
ProductVariant.objects.bulk_create([ProductVariant(name=name) for name in names])
Best practice for this is using bulk_create method.
product_variants = [ProductVariant(name=name) for name in names]
ProductVariant.objects.bulk_create(product_variants)

Django model: Conversation between two users

I'm trying to create a Model which represents a conversation between two users (only two).
Can't figure out how to create two fields because users are equivalent.
class Conversation(models.Model):
user_one = ForeignKey('auth.User')
user_two = ForeignKey('auth.User')
class Meta:
unique_together = ('user_one','user_two')
Is this the best way I can design a model?
And then manager method:
def get_conversation(user_one,user_two):
c = Conversation.objects.filter(Q(user_one=user_one,user_two=user_two)|Q(user_one=user_one,user_two=user_two))
return c
Or is there a more comfortable way to handle such model? For example using ManyToManyField and check if there are two and only two users?:
users = ManyToManyField('auth.User')
Use the related_name field when you have more than 1 foreign key to the same model. Because you often don't care who specifically is user_one and user_two, you can simply make sure that user_one and user_two are consistent. In this case, I'm using the user's id field to say which user will be user_one and which will be user_two. This makes querying simpler because you don't need to do a query for the two pairs (A, B) and (B, A)
class Conversation(models.Model):
user_one = ForeignKey('auth.User', related_name="user_one")
user_two = ForeignKey('auth.User', related_name="user_two")
class Meta:
unique_together = ('user_one','user_two')
def clean(self):
# Ensure that user_one's id is always less than user_two's
if self.user_one and self.user_two and self.user_one.id > self.user_two.id:
(self.user_one, self.user_two) = (self.user_two, self.user_one)
#classmethod
def get(cls, userA, userB):
""" Gets all conversations between userA and userB
"""
if userA.id > userB.id:
(userA, userB) = (userB, userA)
return cls.objects.filter(user_one=userA, user_two=userB)
If you are using postgres you could use an ArrayField:
class Conversation(models.Model):
users = ArrayField(
ForeignKey('auth.User'),
size=2,
)
That would help with lookups. However note what the documentation currently says about the size parameter:
This is an optional argument. If passed, the array will have a maximum size as specified. This will be passed to the database, although PostgreSQL at present does not enforce the restriction.

How can I count the number of instances per a queryset? Django

I'm writing an action to set one field equal to the number of instances attached to it via a foreign key (see below)
Models.py
class competition(models):
competition_name = models.CharField(max_length = 50)
class people(models):
competition = models.ForeignKey(competition)
fullname = models.CharField(max_length = 100)
Admin.py
def people_in_competition:
for X in queryset:
X.number_of_people = X.count(X.set_all) #(I want the number of people in this competition in this part)
X.save()
Of course this gives me an error as I cant seem to use _set_all in admin.py, does it only work in templates? What would be the best way to figure that number out?
Use the backward relation:
X.number_of_people = X.people_set.all().count()

Django ManyToManyField Error when saving in admin?

What is wrong with my code?
class Group(ImageModel):
title = models.CharField(verbose_name = "Title", max_length=7)
photos = models.ManyToManyField('Photo', related_name='+',
verbose_name=_('Photo'),
null=True, blank=True)
.....
pid = Photo.objects.get(image = str_path)
gid= Group.objects.get(id = self.id)
self.save_photos(gid, pid)
....
def save_photos(self, gid, pid):
group_photo = GroupPhotos(groupupload=gid.id,
photo=pid.id
)
group_photo.save()
and my GroupPhotos models is:
class GroupPhotos(models.Model):
groupupload = models.ForeignKey('Group')
photo = models.ForeignKey('Photo')
class Meta:
db_table = u'group_photos'
when i want to save it from admin panel i am getting value error sth like this:
Cannot assign "38": "GroupPhotos.groupupload" must be a "Group" instance.
with group_photo = GroupPhotos(groupupload=gid, photo=pid) defination it is working but there is no any changes in GroupPhotos table(group_photos). printing this print pid.id,' >>> ',gid.id i am getting true relation...
UPDATE:
I have been working since morning, but no progress... i have also tried this but nothing changed:
pid = Photo.objects.get(image = str_path)
ger = Group.objects.get(id = self.id)
ger.title = self.title
ger.save()
ger.photos.add(pid)
The error is here:
group_photo = GroupPhotos(groupupload=gid.id, photo=pid.id)
The arguments to groupupload and photo should be instances of Group and Photo respectively. Try the following:
group_photo = GroupPhotos(groupupload=gid, photo=pid)
In other words, when creating an object you need to pass arguments of the expected type and not an integer (which may be the primary key key of the desired object but it also might not, which is why you need to pass an object of the correct type).
i have solved my problem with adding through option to my manytomanyfield:
photos = models.ManyToManyField('Photo', related_name='+',
verbose_name=_('Photo'),
null=True, blank=True, through=GroupPhotos)
some info about ManyToManyField.through here:
Django will automatically generate a table to manage many-to-many
relationships. However, if you want to manually specify the
intermediary table, you can use the through option to specify the
Django model that represents the intermediate table that you want to
use.
The most common use for this option is when you want to associate extra data with a many-to-many relationship.

Categories

Resources