Using one form for two similar models - python

I have the following models:
class BaseAddress(models.Model):
name = models.CharField(max_length=100)
business_name = models.CharField(max_length=100, blank=True, null=True)
address = models.CharField(max_length=100, blank=True, null=True)
address_2 = models.CharField(max_length=100, blank=True, null=True)
address_3 = models.CharField(max_length=100, blank=True, null=True)
city = models.CharField(max_length=100, blank=True, null=True)
state = models.CharField(max_length=2, blank=True, null=True)
zip_code = models.CharField(max_length=10, blank=True, null=True)
phone = models.CharField(max_length=30, blank=True, null=True)
class Meta:
abstract = True
class ProfileBilling(BaseAddress):
profile = models.OneToOneField(
Profile, related_name='billing_info')
class OrderBilling(BaseAddress):
order = models.OneToOneField(Order, related_name='billing')
name_on_card = models.CharField(max_length=100)
#card_type = models.PositiveSmallIntegerField(
# choices=CARD_TYPE, default=0)
#card_number = models.CharField(
# max_length=16, default=0)
expire_month = models.PositiveSmallIntegerField(
choices=MONTHS, null=True, default=0)
expire_year = models.PositiveSmallIntegerField(
choices=YEARS, null=True, default=1960)
When customers input a billing address, I want to save it in OrderBilling, but I also want to save it in ProfileBilling as their most recent billing address. How do I do so?
How do I go about using forms to save billing address in two different tables when the OrderBilling and ProfileBilling have most of the same fields...?
How do I do this in Django?
Here is my OrderBilling form:
class OrderBillingForm(forms.ModelForm):
class Meta:
model = OrderBilling
exclude = ('order',)
def __init__(self, *args, **kwargs):
super(OrderBillingForm, self).__init__(*args, **kwargs)
self.fields['address'].required = True
self.fields['city'].required = True
self.fields['state'] = USStateField()
self.fields['zip_code'] = us.USZipCodeField()
self.fields['phone'].required = False
self.fields['expire_month'].required = False
self.fields['expire_year'].required = False
def clean(self):
return self.cleaned_data

You can override save() method, But the smarter way in your case would be using post_save signal for this purpose.
After aOrderBilling get saved, You can save its data into ProfileBilling too.
look at some example on google search in case to be familiar with post_save signal,
like:
https://groups.google.com/forum/?fromgroups=#!topic/django-users/2m88qTrqnM8
http://www.djangofoo.com/85/signal-connect-disconnect-django-signals
etc...
then Easyily in your post_save receiver|callback funcstion get the OrderBilling instance
orderbil_instance = kwargs['instance']
And create your ProfileBilling from its data
ProfileBilling.objects.create(name=orderbil_instance.name, ....)
Something like on post_save signal receiver
def do_something(sender, **kwargs):
# Getting OrderBilling instance which get saved just now
orderbil_instance = kwargs['instance']
# Save the data into new ProfileBilling
ProfileBilling.objects.create(name=orderbil_instance.name, ....)

Related

django rest framework serializer method field how to

How do I handle this serializer method field? I feel that using initial_data is wrong, do i need to validate data when extracting it from the database? Do I need to use a serializer when extracting data from database?
class ListingSerializer(serializers.ModelSerializer):
rooms = serializers.SerializerMethodField()
# This one works as expected
def get_rooms(self, obj):
rooms = list(Room.objects.filter(listing__id=obj.id).values())
serializer = RoomSerializer(data=rooms, many=True)
return serializer.initial_data
# This one gives serializer errors
def get_rooms(self, obj):
rooms = list(Room.objects.filter(listing__id=obj.id).values())
serializer = RoomSerializer(data=rooms, many=True)
if serializer.is_valid():
return serializer.data
return serializer.errors
class Meta:
model = Listing
fields = "__all__"
class Listing(models.Model):
id = models.CharField(
primary_key=True, default=generate_uuid, editable=False, max_length=36
)
agent = models.ForeignKey(
"users.User", on_delete=models.CASCADE, blank=True, null=True
)
# property data
title = models.CharField(max_length=100, blank=False, null=False)
description = models.CharField(max_length=1000, blank=False, null=False)
floor = models.IntegerField(blank=False, null=False)
floor_count = models.IntegerField(blank=False, null=False)
price = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal(0.00))
# address
street = models.CharField(max_length=60, blank=False, null=True)
house_no = models.CharField(max_length=10, blank=False, null=True)
door_no = models.CharField(max_length=10, blank=False, null=True)
city = models.CharField(max_length=20, blank=False, null=True)
country = models.CharField(max_length=20, blank=False, null=True)
postal_code = models.IntegerField(blank=False, null=True)
# property reports
tilstand_report = models.FileField(upload_to="reports", blank=False, null=True)
water_consumption_report = models.FileField(
upload_to="reports", blank=False, null=True
)
energy_level_report = models.FileField(upload_to="reports", blank=False, null=True)
property_tax_report = models.FileField(upload_to="reports", blank=False, null=True)
# metadata
is_active = models.BooleanField(default=True)
create_time = models.BigIntegerField(blank=True, null=True)
def save(self, *args, **kwargs):
self.create_time = int(datetime.now().timestamp() * 1000)
super().save(*args, **kwargs)
class Meta:
ordering = ["create_time"]
You can use the RoomSerializer as a sub serializer:
class ListingSerializer(serializers.ModelSerializer):
rooms = RoomSerializer(source='room_set', many=True)
class Meta:
model = Listing
fields = '__all__'
The source=… should specify the related_name=… of the ForeignKey in the Room model. If you did not specify a related_name=…, the default is modelname_set, so here room_set.

How can we notify the admin about newly added project by user, by default project is_active=False

How the admin will be notified on the dashboard (not by mail), and verify all the projects or can decline the projects.
class Compaigns(models.Model):
nameOfDeceased = models.CharField(max_length=100, null=False, blank=False)
nameOfDeceasedEn = models.CharField(max_length=100, null=False, blank=False)
projectName = models.CharField(max_length=255, null=False, blank=False)
projectNameEn = models.CharField(max_length=255, null=False, blank=False)
phone = models.CharField(max_length=255, null=False, blank=False)
image = models.ImageField(upload_to='projects/compaigns/%Y/%m/%d', blank=True, null=True)
is_compaign = models.BooleanField(default=True)
**is_active = models.BooleanField(default=False)**
detail = models.TextField(blank=True, null=True)
detailEn = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
suggestedDonation = models.DecimalField(
max_digits=10, decimal_places=3, default=0.000)
compaignCategory = models.ManyToManyField(CompaignCategory)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.nameOfDeceased
class Meta:
verbose_name_plural = "Compaigns"
You can achieve this by:
Overriding models.Model's save() method
Using post_save signal
by overriding save() method:
class Compaigns(models.Model):
# ... Your model's fields
def __str__(self):
return self.nameOfDeceased
class Meta:
verbose_name_plural = "Compaigns"
def save(self, *args, **kwargs):
super().save(*args, **kwargs) # Call the "real" save() method.
send_notification() # your method to send notification to admin
Using signal is a little more complicated than overriding save() method. For further reading I added django docs link.

automatically add a new models to admin page

i have a calculator app, inside it i have a Transaction , Family_group , Family_member models see pic below.
i want everytime i try to make a new Transaction there will be a default 1 Family_group and Family_member added automatically, without me starting one each time. is there any way to do it ?
models.py
class Transaction(models.Model):
income_period_choices = (('Weekly', 'Weekly'), ('Fortnightly',
'Fortnightly'))
chp_reference = models.CharField(max_length=50, unique=True)
rent_effective_date = models.DateField(null=True, blank=True)
income_period = models.CharField(max_length=11,
choices=income_period_choices,
null=True,
blank=True, default='Weekly')
property_market_rent = models.DecimalField(help_text='Weekly',
max_digits=7,
decimal_places=2,
null=True,
blank=True)
class FamilyGroup(models.Model):
name_choices = (('FG_1', 'FG_1'), ('FG_2',
'FG_2'), ('FG_3', 'FG_3'), ('FG_4',
'FG_4'), ('FG_5', 'FG_5'))
name = models.CharField(max_length=10, choices=name_choices)
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE)
family_type = models.ForeignKey(FamilySituation,
on_delete=models.PROTECT,
null=True,
blank=True)
class FamilyMember(models.Model):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE)
family_group = models.ForeignKey(FamilyGroup,
on_delete=models.CASCADE,
null=True,
blank=True)
name = models.CharField(max_length=100, null=True, blank=True)
date_of_birth = models.DateField(null=True, blank=True)
relationship = models.ForeignKey(Relationship, on_delete=models.PROTECT)
Without seeing your code, it's hard to help, but this might get you started:
You can override the save method on the Transaction model like so:
class Transaction(models.Model):
...
def save(self, *args, **kwargs):
# add / create family group if it is missing:
if not self.family_group:
self.family_group = <the Family_group instance>
# add / create family member if it is missing:
if not self.family_group.family_member_set.all()
self.family_group.add([<the Family_member instance>])
self.family_group.save()
return super(Transaction, self).save(*args, **kwargs)

How can i get in choices=max_guest by room_id when I create django.models?

I have model like this:
class KeyTransfer(Model):
key_out_data = DateTimeField(null=True, blank=True)
key_in_data = DateTimeField(null=True, blank=True)
room_id = ForeignKey(Room, blank=True, null=True, on_delete=CASCADE)
guests = IntegerField(choices=[(x, str(x)) for x in range(Room.objects.get(number=room_id).max_guests)], blank=True, null=True)
notes = CharField(max_length=256, blank=True)
person_id = ForeignKey(Person, null=True, blank=True, on_delete=SET_NULL)
and i understand that i can`t save some value in column "guests" with argument "choices=" as you can see above.
In a consequence I have gotten error:
...django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet. ...
and i see what this error means.
The question is: can I realise some similar condition for "choices" in "guests" do not using django.forms?
It seems that you want to set upper limit for guests field of KeyTransfer model based on its related room model's max_guests field. You can do that by overriding save method and check there if assigned guests is higher than max_guests. See below implementation of this approach:
from django.db import models
from django.core.exceptions import ValidationError
class Person(models.Model):
name = models.CharField(max_length=32)
def __str__(self):
return "<Person {}>".format(self.name)
class Room(models.Model):
max_guests = models.IntegerField()
class KeyTransfer(models.Model):
key_out_data = models.DateTimeField(null=True, blank=True)
key_in_data = models.DateTimeField(null=True, blank=True)
room_id = models.ForeignKey('Room', blank=True, null=True, on_delete=models.CASCADE)
guests = models.IntegerField(blank=True, null=True)
notes = models.CharField(max_length=256, blank=True)
person_id = models.ForeignKey('Person', null=True, blank=True, on_delete=models.SET_NULL)
def save(self, *args, **kwargs):
room = self.room_id
if self.guests > room.max_guests:
raise ValidationError("Assigned guests exceeding related room's maximum limit of {}"\
.format(room.max_guests))
super().save(*args, **kwargs)

django - Runpython function to turn charfield into foreignkey

I've been strugglin to relate a csv imported data model with a spatial data model based on a CharField.
I've created both models and now im trying to transfer the data from one field to a new one to be the ForeignKey field. I made a Runpython funtion to apply on the migration but it goives the an error:
ValueError: Cannot assign "'921-5'":
"D2015ccccccc.rol_fk" must be a "D_Base_Roles" instance.
Here are the models:
class D_Base_Roles(models.Model):
predio = models.CharField(max_length=254)
dest = models.CharField(max_length=254)
dir = models.CharField(max_length=254)
rol = models.CharField(primary_key=True, max_length=254)
vlr_tot = models.FloatField()
ub_x2 = models.FloatField()
ub_y2 = models.FloatField()
instrum = models.CharField(max_length=254)
codzona = models.CharField(max_length=254)
nomzona = models.CharField(max_length=254)
geom = models.MultiPointField(srid=32719)
def __str__(self):
return str(self.rol)
class Meta():
verbose_name_plural = "Roles"
class D2015ccccccc(models.Model):
id = models.CharField(primary_key=True, max_length=80)
nombre_archivo = models.CharField(max_length=180, blank=True, null=True)
derechos = models.CharField(max_length=120, blank=True, null=True)
dir_calle = models.CharField(max_length=120, blank=True, null=True)
dir_numero = models.CharField(max_length=120, blank=True, null=True)
fecha_certificado = models.CharField(max_length=50, blank=True, null=True)
numero_certificado = models.CharField(max_length=50, blank=True, null=True)
numero_solicitud = models.CharField(max_length=50, blank=True, null=True)
rol_sii = models.CharField(max_length=50, blank=True, null=True)
zona_prc = models.CharField(max_length=120, blank=True, null=True)
##NEW EMPTY FOREIGNKEY FIELD
rol_fk = models.ForeignKey(D_Base_Roles, on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return str(self.numero_certificado)
class Meta:
managed = True
#db_table = 'domperm2015cip'
verbose_name_plural = "2015 Certificados Informaciones Previas"
ordering = ['numero_certificado']
The Runpython function:
def pop_rol(apps, schema_editor):
roles = apps.get_model('b_dom_edificacion', 'D2015ccccccc')
for r in roles.objects.all():
rol = roles
r.rol_fk = r.rol_sii
r.save()
D_Base_Roles.rol values are all unique, and 921-5 is one of those values.
What am I missing?
You probably need to assign an object, not a string. Change the line
r.rol_fk = r.rol_sii
to
r.rol_fk = D_Base_Roles.objects.get(rol=r.rol_sii)
Maybe adjust to whatever the correct field for looking up D_Base_Roles instances is.
Note: this will make a database query for every iteration of the for-loop

Categories