Dynamic django choice field - python

I have 4 models: Products (the list of products: freezers, microwaves, tvs and pcs), ProductType (entertainment and home appliances), Credit (a credit is registered on each purchase) and PurchaseReason (the reason why the customer has bought the product).
The PurchaseReason depend on the productType, so the purchaseReason has a foreignKey field productType.
In addition, each credit has a product as foreignKey and a purchaseReason as foreignKey.
Also, I have the ProductReason field as a choice field in the credit model, and I want the options to be set dynamically based on the product field of the credit model.
I'm creating an API so I think this cant be handle with modelForms, but i'm not sure. The hard work would be with the serializers (DRF) and with the django-admin (specially this one because in my product the django admin plays an important role)
What would be the best approach to manage my models in Django?
Here are my models. In credit I'm not sure how to implemente the purchase reason:
class Credit(models.Model):
client = models.ForeignKey('clients.Client', on_delete=models.SET_NULL)
name = models.CharField(max_length=30, null=False, blank=True)
product = models.ForeignKey('product',on_delete=models.SET_NULL)
reason_purchase = models.ChoiceField(????)
class PurchaseReason(models.Model):
product_type = models.ForeignKey(product_type, on_delete=models.CASCADE)
reason = models.CharField(max_length=30, null=False, blank=True)
class ProductType(models.Model):
name = models.CharField(max_length=30, null=False, blank=False)
class Product(models.Model):
model = models.CharField(max_length=30, default=None, null=True)
product_type = models.ForeignKey(product_type, on_delete=models.CASCADE)

When we use the foreign key, we need to mention the model name of that particular model so that we can integrate that particular model in that model as a reference entity. Have a look at this example.
from django.db import models
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
def __str__(self):
return self.headline
class Meta:
ordering = ['headline']
you've not mentioned the model name properly. it should be Product in place of 'product' in the Credit class, product field.
use this reference https://docs.djangoproject.com/en/3.2/topics/db/examples/many_to_one/
i think you should be able to use the Foreignkey field properly after this. Although, if you can't, you can share the actual objective. i will help you to write the correct model.
Best wishes :)

Related

Django: Defining 'present_company' from 'company_name' ManytoManyField

I have 2 models, Company and Employee, and defined company history as a ManytoManyField. I am trying to save the present company name of the employee which I get from the company name ManytoManyField. What should be the method to save it?
This is what I tried:
I tried to override the save method in Models.
models.py
from django.db import models
class Company(models.Model):
name = models.CharField(max_length=100)
def __str__(self) -> str:
return self.name
class Employee(models.Model):
name = models.CharField(max_length=100)
company_name = models.ManyToManyField(Company, blank=True)
present_company = models.CharField(max_length=100, blank=True)
def save(self):
super(Employee, self).save()
last_index = self.company_name.count()-1
self.present_company=str(Company.objects.filter(employee__name=self.name)[last_index])
super(Employee, self).save()
def __str__(self) -> str:
return self.name
I face two problems with this method:
I add/edit the models from the admin site, when I try to save the models and print the names of the company, it prints out the previous edit and not the latest one.
The order of the companies is not according to the order in which I save the models.
So, what could be the changes in this method, or, some other method to do this job.
As #Sahil mentions in comments that he wants the ordering based on employees joined the company.
when you use a Many-to-Many field Django in the backend created a separate table for mapping(through table). you can see this table in DB. now you want the details of Employee joined you can create a table(this table is just same as Many-to-Many field but with extra fields such as joined_in etc.) with details such as follow:
class EmployeeCompnyMap(models.Model):
employee = models.ForeignKey(
Employee, on_delete=models.CASCADE, related_name='employee_company_map')
company = models.ForeignKey(
Company, on_delete=models.CASCADE, related_name='company_employee_map')
is_active = models.BooleanField(default=True)
joined_on = models.DateField(auto_now_add=True)
resigned_on = models.DateField(blank=True, null=True)
Now if you want second_latest you can do the following in your save model:
last_company = self.employee_company_map.all().order_by('-resigned_on')[1]

create django user based on existing model object

I'm working on Property Management django app where my base model is Tenant with all basic info like name, surname, email etc. Willing tenants will be able to create user account so they can log in and book Gym/Cinema, but not all Tenants will need to have user account. My problem is:
How can I create new user accounts based on existing Tenant objects? Obviously user will have Tenant ForeignKey but how can I extract Tenant.name, Tenant.surname etc to more than 1 field in my user model?
ForeignKey only gives me reference to object but can I somehow access certain fields of that object during creation of new user so I make sure that Tenant.email is the same as user.email?
Edit
tenancy = (('Tenant', 'Tenant'),('Owner', 'Owner'), ('Other', 'Other'))
class Tenant(models.Model):
name = models.CharField(max_length=15, null=True, blank=False)
surname = models.CharField(max_length=30, null=True, blank=False)
email = models.EmailField(max_length=50, unique=True)
phone_number = models.CharField(max_length=20, null=True, blank=True, unique=True)
flat = models.ForeignKey(Flat, on_delete=models.PROTECT)
status = models.CharField(max_length=10, choices=tenancy, null=True, blank=False)
stay_length = models.CharField(max_length=20, null=True, blank=False)
pet_licence = models.CharField(max_length=20, null=True, blank=False)
additional_notes= models.TextField(max_length=300, blank=True)
date_added = models.DateField(auto_now_add=True, null=True)
moved_out = models.BooleanField(default=False)
date_moved_out= models.DateField(auto_now_add=False, null=True)
class Meta:
unique_together = ('name', 'surname',)
def __str__(self):
return f'{self.name} {self.surname}'
Now I'd like to create user account model where name, surname, email, phone_number and flat will be ForeignKeys of Tenant model. Is it even possible to have 4 ForeignKeys from 1 object populating new model?
I've tried playing around with ForeignKey.limit_choices_to, ForeignKey.related_name, ForeignKey.to_field (this was close but field related to has to be unique which doesn't work for my case) but everything gives errors. I just want to find out if it's even possible that more than 1 ForeignKey of 1 object can be directed to multiple different fields of new model object.
I would approach it in a way that the foreign key is in Tenant, instead of User, and define it as a nullable one-to-one. This way you keep your User model free of foreign keys:
class Tenant(models.Model):
user = models.OneToOneField(
User,
related_name='tenant',
on_delete=models.CASCADE,
null=True,
blank=True,
default=None,
)
Then to create the related user, you can add a method in your Tenant model like so:
class Tenant(models.Model):
...
def create_user(self):
if not self.user:
user = User.objects.create(
first_name=self.name,
last_name=self.surname,
...
)
self.user = user
self.save()
Have a look at the example from the docs here.
You can use to_field to create fkeys to non-pk fields of another model, however, those fields need to have a unique constraint (i.e. unique=True) - which seems unlikely for name/surname.
It sounds like you want a transparent access from the User model to the Tenant models fields, and that is not possible.
You can create a fkey from User to Tenant:
class User(models.Model):
tenant = models.OneToOneField(Tenant, null=True, blank=True, related_name='user')
...
and then access the fields with
user = User.objects.get(...)
user.tenant.surname
to keep the fields in sync you can override the save() method:
class Tenant(...)
...
def save(self, *args, **kwargs):
if self.user:
self.user.last_name = self.surname
...
self.user.save()
super().save(*args, **kwargs)
aside: null=True indicates that the database should allow null in the field, blank=True says that the field can be empty in the admin interface. You should probably have null=True, blank=True in most cases.

How do I model a many to many relationship in Django Rest Framework?

I need to model a many to many relationship.
I've read some documentation, but I don't know how to model.
I'll give you an example of what I want to do.
I have two entities, Album and song.
They have a many to many relationship.
class Song(models.Model):
name = models.CharField(unique=True, max_length=255)
class Album(models.Model):
nombre = models.CharField(unique=True, max_length=255)
songs = models.ManyToManyField(Song, blank=True)
The user in the frontend, provides me the data of an album that I must save.
It provides me the name of the album and the name of the songs.
How could I model it on the serializers?
Nothing runs.
I think you can delete that blank=True i think it automatically can be blank.
class Song(models.Model):
name = models.CharField(unique=True, max_length=255)
class Album(models.Model):
nombre = models.CharField(unique=True, max_length=255)
songs = models.ManyToManyField(Song, related_name='songs', blank=True)
and you can make the serializer.
Django will create a table for many to many relationships. You can do this by yourself as well with customized fields. For example, you may like to save the order for songs in an album:
class Song(models.Model):
name = models.CharField(unique=True, max_length=255)
class Album(models.Model):
name = models.CharField(unique=True, max_length=255)
class AlbumSong(models.Model):
album = models.ForeignKey('Album', null=False, on_delete=models.PROTECT)
song = models.ForeignKey('Song', null=False, on_delete=models.PROTECT)
order = models.PositiveIntegerField(null=False)
removed = Bit1BooleanField(null=False, default=False)

How to fix manytomany field in django

How to make a one to many relationship in Django/Mysql?
I have an identical situation to this post, yet, my django returns errors on the admin page:
get() returned more than one order2pizza-- it returned 5!
order2pizza with that pizza already exists.
My mysql database have composite keys on a tertiary table to order and pizza to link multiple pizzas to an order.
models.py:
class Orders(models.Model):
order_id = models.AutoField(primary_key=True)
order_name = models.CharField(max_length=100, blank=True, null=True)
class Pizza(models.Model):
Pizza= models.AutoField(primary_key=True)
Pizza_name= models.CharField(max_length=50, blank=True, null=True)
class order2pizza(models.Model):
order = models.ManyToManyField(Orders, models.DO_NOTHING, )
pizza_id= models.IntegerField()
class Meta:
unique_together = (('order ', 'pizza_id'),)
A many-to-many relation can be expressed in two ways. First, you can manually specify a "join" model, like this:
class Orders(models.Model):
order_name = models.CharField(max_length=255, blank=True)
class Pizza(models.Model):
Pizza_name= models.CharField(max_length=255, blank=True)
class Order2Pizza(models.Model):
order = models.ForeignKey(Order, models.CASCADE)
pizza = models.ForeignKey(Pizza, models.CASCADE)
class Meta:
unique_together = ['order ', 'pizza']
This is useful if you want to put extra fields on the Order2Pizza model. A field named quantity would be very useful in your example.
The second option is to use a ManyToManyField. This will automatically create the join model for you:
class Orders(models.Model):
order_name = models.CharField(max_length=255, blank=True)
pizzas = models.ManyToManyField('Pizza', related_name='orders')
class Pizza(models.Model):
Pizza_name= models.CharField(max_length=255, blank=True)
In your original question you put the ManyToManyField on the Order2Pizza model, which is nonsensical.
However, the source of your bug is probably your manual inclusion of several *_id fields. Don't do that. They will always be created automatically by Django and you should never have to specify them manually. Instead, try the two options above and see how they work.

Django ModelForm for Multiple Categories of a Product in EAV data model

Hello all I am making auction website like ebay, I have this model design which has many other extra attributes model classes for different categories. But here let's take an example of a PC one which will be used for its sub-categories Desktop and Laptop.
Now the problem, I want to create ModelForm for users. For instance if user selects Desktop as their product to put on auction, how will that modelform code look like? So the Desktop respected fields are given to the user to fill from the extra_pc_attributes class? The problem is that, wouldn't it get tedious to write separate modelform for each category and also in the views.py use those as objects.
Maybe use Jsonfield instead of creating a whole EAV old-fashioned table for extra attributes? But I am new and I don't know how it will work or even if it applies to this situation.
class Categories(MPTTModel):
name = models.CharField(max_length=50, unique=True)
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
class auction_product(models.Model):
product_name = models.CharField(max_length=64)
category = models.ForeignKey(Categories, on_delete=models.CASCADE)
date_added = models.DateField(default=timezone.now)
user = models.ManyToManyField(User, through='product_ownership', related_name='product_user')
product_bid = models.ManyToManyField(User, through='bid', related_name='product_bid')
product_comment = models.ManyToManyField(User, through='comment')
album = models.OneToOneField(ImageAlbum, related_name='product_model', on_delete=models.CASCADE)
def __str__(self):
return self.product_name
#Extra PC Attributes
class extra_pc_attributes(auction_product):
processor = models.CharField(max_length=264)
ram = models.FloatField()
brand = models.CharField(max_length=64)
motherboard = models.CharField(max_length=264)
case = models.CharField(max_length=264)
screen_size = models.FloatField()
weight = models.FloatField()

Categories