I'm struck on saving m2m relation.
models.py
class BasicTag(models.Model):
name = models.CharField(max_length=150, verbose_name="Tag Name")
image_class = models.CharField(max_length=30, blank=True)
color = models.CharField(max_length=10, blank=True)
def __unicode__(self):
return self.name
class ExtendedTag(models.Model):
parent = models.ManyToManyField(BasicTag, blank=True,
related_name="parent")
category = models.ManyToManyField(BasicTag, blank=True,
related_name="category")
def __unicode__(self):
return self._id
class CombineTag(BasicTag, ExtendedTag):
"""
"""
forms.py
class CombineTagForm(forms.ModelForm):
class Meta:
model = CombineTag
Now when I initialize form as
>form = CombineTagForm({"name":"shyam"})#this don't have any parent i.e. root tag
>form.is_valid()
True
>instance = form.save(commit = False)
>instance.save()
>form.save() #return errors
#error
ProgrammingError: column tag_extendedtag_parent.basictag_id does not exist
LINE 1: DELETE FROM "tag_extendedtag_parent" WHERE "tag_extendedtag_...
>form.save_m2m() #return errors ... struck here
So How should I need to save m2m field modelform. I had follow official doc which say that
If your model has a many-to-many relation and you specify commit=False when you save a form, Django cannot immediately save the form data for the many-to-many relation. This is because it isn’t possible to save many-to-many data for an instance until the instance exists in the database.
But I couldn't able to figure out what I am missing here.
Related
I am trying to update a model field by overriding save method:
class DataTable(models.Model):
id = models.AutoField(db_column='Id', primary_key=True)
country= models.ForeignKey(Countries,on_delete=models.DO_NOTHING,db_column='CountryId')
university=models.ManyToManyField(Universities,db_column='UniversityId',verbose_name='University',related_name='universities')
intake = models.CharField(db_column='Intake',blank=True, null=True, max_length=20, verbose_name='Intake')
year = models.CharField(max_length=5,blank=True,null=True)
application_status = models.ForeignKey(Applicationstages,on_delete=models.DO_NOTHING, db_column='ApplicationStageId',verbose_name='Application Status')
requested_count = models.PositiveIntegerField(db_column='RequestedCount',blank=True,null=True)
class Meta:
db_table = 'DataTable'
def __str__(self):
return str(self.application_status)
def __unicode__(self):
return str(self.application_status)
def save(self,*args, **kwargs):
super(DataTable,self).save(*args, **kwargs)
country_id= self.country
intake= self.intake
year= self.year
universities= self.university.all()
courses = get_ack_courses(country_id,universities)
all_data = get_all_courses(intake,year,courses)
ack_data = get_acknowledgements_data(all_data)
self.requested_count =len(ack_data)
super(DataTable,self).save(*args, **kwargs)
I am trying to update the requested_count field using the other field values, but in save method when I try to get the m2m field data ; it is returning empty. I tried with post_save signal also and there also m2m field data is empty.
I want the count field based on otherfields from this model. How to save when we have m2m fields?
I want to overwrite a creation of M2M through model object. I thought that overwriting save method will be sufficient, but it appears that after saving admin form, the method is not called. I am having a hard time finding how this object is created.
Here is the code snippet
class ProductVariantToAttributeValue(models.Model):
product_variant = models.ForeignKey(ProductVariant, on_delete=models.CASCADE)
attribute_value = models.ForeignKey(AttributeValue, on_delete=models.CASCADE)
attribute = models.ForeignKey(
Attribute, on_delete=models.CASCADE, null=True, blank=True
)
class Meta:
db_table = "productvariants_to_attributevalues"
unique_together = [("product_variant", "attribute")]
verbose_name_plural = "Product Variants To Attribute Values"
def save(self, **kwargs):
self.attribute = self.attribute_value.attribute
super().save(**kwargs)
In my django app, there is something strange that's happening & i'm not understanding.
I have two different tables (employeeProfile & purchaserShippingDetail) each has a field with the relation OneToOneField but with the 1st table (employeeProfile) in the field user that uses OneToOneField i can pass a string representation say Michael using api & i don't get an error but in my 2nd table that has similar structure when i add a string representation to i get
IntegrityError at /api/clients/shipping/
null value in column "owner_id" violates not-null constraint
1st Table model (works fine)
class employeeProfile(models.Model):
image = models.ImageField(default='default.png',upload_to='employee_photos/%Y/%m/%d/')
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name="employee_profile")
phone_no = models.CharField(max_length=10, unique=True)
def __str__(self):
return self.user.name
2nd Table Model (The one that throws the "owner_id" violates not-null constraint error)
class purchaserShippingDetail(models.Model):
frequent_customer = models.BooleanField(default=False)
owner = models.OneToOneField(Purchaser, on_delete=models.CASCADE, related_name="purchaser_shipping")
address = models.CharField(max_length=12, blank=True)
zip_code = models.CharField(max_length=12, blank=True)
location = models.CharField(max_length=255)
def __str__(self):
return self.owner.name
Purchaser Model
class Purchaser(models.Model):
name = models.CharField(max_length=50)
phone = models.CharField(max_length=20, unique=True)
email = models.EmailField(max_length=255, unique=True, blank=True)
data_added = models.DateField(default=datetime.date.today)
def __str__(self):
return self.name
serializer for purchaserShippingDetail model
class purchaserShippingDetailSerializer(serializers.ModelSerializer):
owner = serializers.StringRelatedField(read_only=True)
class Meta:
model = purchaserShippingDetail
fields = '__all__'
Views.py for purchaserShippingDetail model
class purchaserShippingDetailsListCreateView(ListCreateAPIView):
serializer_class = purchaserShippingDetailSerializer
queryset = purchaserShippingDetail.objects.all()
EDIT: Added Purchaser model table
Could you please post the Purchaser model as well? There is a referenced field owner_id that we can't see in your post, which will explain more.
Have you added the owner field in a recent migration? It could be that you are adding a non-nullable field to a Table with existing rows, making those rows fail to satisfy the non-nullable condition.
You cannot add a default value on a OneToOneField so in that case you have to first add the field as null=True.
Then create an empty migration file to instantiate all rows on that table so no rows have a null value. Normally you do this by manage.py makemigrations app_name --empty.
That file could look something like
from django.db import migrations
def instantiate_owner(apps, schema_editor):
purchaserShippingDetail = apps.get_model("appname", "purchaserShippingDetail")
Purchaser = apps.get_model("some_other_app", "Purchaser")
for detail in purchaserShippingDetail.objects.all():
owner = Purchaser.objects.get(some_unique_criteria=detail.unique_criteria)
detail.owner = owner
detail.save()
class Migration(migrations.Migration):
dependencies = [
...,
]
operations = [
migrations.RunPython(instantiate_owner),
]
After that is complete you can remove the null=True in your model and make another migration.
Is it possible to define a foreign key or OneToOne relation in django model with only subset of data?
For example :
I have 2 models.
#with_author
class Product(models.Model):
GTIN = models.CharField(max_length=30)
material = models.ForeignKey(Material, on_delete=models.PROTECT)
UOM = models.OneToOneField(MaterialUOM)
defaultPrice = MoneyField(max_digits=10, decimal_places=2, default_currency='USD')
and
#with_author
class UOM(models.Model):
uomname = models.CharField(max_length=30)
material = models.ForeignKey(Material, on_delete=models.PROTECT)
so I want in my Product model only to allow UOM values that have same material value as in product.
Is it possible on model level or any other place and not to display non relevant values in the dropdown?
You can enforce this constraint by adding some validation to the model's clean() method. Something like:
from django.core.exceptions import ValidationError
class Product(models.Model):
GTIN = models.CharField(max_length=30)
material = models.ForeignKey(Material, on_delete=models.PROTECT)
UOM = models.OneToOneField(MaterialUOM)
defaultPrice = MoneyField(max_digits=10, decimal_places=2, default_currency='USD')
def clean(self):
if not self.material == self.UOM.material:
# This will cause the model not to be saved and report an error
raise ValidationError('Material does not match UOM material')
If you are using a ModelForm to handle edits to your models, then clean() will be called automatically as part of the form validation. If you are modifying models directly in your code, then you need to call it yourself before saving the model. The documentation explains this in detail.
If you want to be doubly sure, you can also override the save() method:
def save(self, *args, **kwargs):
if not self.material == self.UOM.material:
return # Model is not saved
super(Product, self).save(*args, **kwargs)
This will not report any errors - it will just not save the model. Hence you should also use the clean() method above.
Problem Statement:
I am using Django admin to manage many tables, some of which have many-to-many relationships. I am unable to save new records in tables (models) that have manytomany fields defined. I am able to render the add form just fine. The problem is only upon trying to save the record. I do not have the same problem when updating an existing record.
Using the models below, I receive the following error: 'Bout' instance needs to have a primary key value before a many-to-many relationship can be used.
The Bout model has a many-to-many relationship with the Equipment model. The BoutEquipment model is the intermediate model.
I've researched this problem high and low on StackOverflow and via Google, but am thus far unable to find a solution.
Disclosure: I'm new to Django and new-ish to Python. I'm hopeful that there's a relatively simple solution to this problem.
Thanks in advance.
models.py
class Bout(models.Model):
boutid = models.AutoField(db_column=u'BoutID', primary_key=True)
sessionid = models.ForeignKey(Session, db_column=u'SessionID', verbose_name=u'Session')
activitytypeid = models.ForeignKey(Activitytype, db_column=u'ActivityTypeID', verbose_name=u'Activity Type')
locationid = models.ForeignKey(Location, db_column=u'LocationID',verbose_name=u'Location')
equipment = models.ManyToManyField(Equipment, verbose_name=u'Related Equipment', related_name=u'Bout_Equipment', blank=True, null=True) #through = 'BoutEquipment'
intensitymetrics = models.ManyToManyField(Intensitymetric, verbose_name=u'Related Intensity Metrics', related_name=u'Bout_IntensityMetrics', blank=True, null=True) #through = 'BoutIntensitymetric'
def __unicode__(self):
return u'%s %s' % (self.sessionid, self.activitytypeid)
class Meta:
db_table = u'app_bout'
verbose_name = u'Bout'
verbose_name_plural = u'Bouts'
class Equipment(models.Model):
equipmentid = models.AutoField(db_column=u'EquipmentID', primary_key=True)
name = models.CharField("Name", max_length=100, db_column=u'Name')
equipmenttypeid = models.ForeignKey(Equipmenttype, db_column=u'EquipmentTypeID', verbose_name = u'Equipment Type')
def __unicode__(self):
return self.name
class Meta:
db_table = u'app_equipment'
verbose_name = u'Equipment'
verbose_name_plural = u'Equipment'
class BoutEquipment(models.Model):
id = models.AutoField(db_column=u'id', primary_key=True)
boutid = models.ForeignKey(Bout, db_column=u'Bout_ID')
equipmentid = models.ForeignKey(Equipment, db_column=u'Equipment_ID')
def __unicode__(self):
return self.name
class Meta:
db_table = u'app_bout_equipments'
admin.py
class EquipmentAdmin(admin.ModelAdmin):
form = EquipmentForm
inlines = [EquipmentShoeInline, EquipmentLogInline]
list_display = ('name','equipmenttypeid','manufacturer','retired','retiredby','retiredon','notes')
fields = (
'name',
('equipmenttypeid','manufacturer'),
('retired','retiredby','retiredon'),
'notes'
)
class BoutAdmin(admin.ModelAdmin):
form = BoutForm
filter_horizontal = ('equipment','intensitymetrics',)
list_display = ('sessionid','activitytypeid','locationid','sequence','activehand','baddata')
inlines = [BoutDeviceInline,]
fields = (
('sessionid','locationid','activitytypeid'),
'videofilelocation',
'sequence',
'activehand',
'notes',
'baddata',
('equipment','intensitymetrics')
)
A manytomany field in django is a join table between the two models you want to connect to each other.
This happens on SQL level, so both models have to exist in the database.
bout = Bout()
...
equipment = Equipment()
...
bout.equipment.add(equipment)
#fails because bout and equipment are not saved
bout.save()
bout.equipment.add(equipment)
#fails because equipment is not saved
equipment.save()
bout.equipment.add(equipment)
#yay :)