I'm trying to create a model method that returns an attribute of a foreign key. In this case, I want get_place_name() to return the pretty_name field of the place model. But when I do so, I get an attribute error: "'NoneType' object has no attribute 'pretty_name'"
class CalendarEvent(models.Model):
event_id = models.CharField( max_length=22, db_index=True, unique=True )
event_name = models.CharField( max_length=255, db_index=True )
place = models.ForeignKey( Place, blank=True, null=True )
def __unicode__(self):
return self.event_name
def get_place_name(self):
return "%s" % self.place.pretty_name
Well, you have null=True for your place foreign key. Is it possible that for the particular CalendarEvent that you are calling, place is indeed None? In which case the error would be accurate. You could handle that more gracefully by modifying your get_place_name method:
def get_place_name(self):
if self.place:
return self.place.pretty_name
Related
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)
I have 2 tables. I wanted to filter records from the second table based on filtered value from the first table. There is something wrong in my second filter statement. If anyone can help me to sort it out?
report_table_data=report_table.objects.filter(cn_number=Master_table_data.cn_number))
My codes are as below.
Models.py
class Master_table(models.Model):
sl_no = models.AutoField(primary_key=True)
cn_number = models.CharField(max_length=10, null=False)
class report_table( models.Model ):
cn_number = models.ForeignKey(Master_table, on_delete=models.CASCADE)
remarks = models.CharField( max_length=20, null=False )
Views.py
def any_view(request):
Master_table_data=Master_table.objects.get(sl_no=request.GET['key'])
report_table_data=report_table.objects.filter(cn_number=Master_table_data.cn_number))
This throws below error.
ValueError: invalid literal for int() with base 10: 'USS2000203'
I realized later that, I made mistake in my "report_table".
I changed the codes as below and it is working.
models.py
class Master_table(models.Model):
sl_no = models.AutoField(primary_key=True)
cn_number = models.CharField(max_length=10, null=False)
class report_table( models.Model ):
# I corrected below field name from "cn_number" to "sl_no"
sl_no = models.ForeignKey(Master_table, on_delete=models.CASCADE)
remarks = models.CharField( max_length=20, null=False )
views.py
def any_view(request):
Master_table_data=Master_table.objects.get(sl_no=request.GET['key'])
report_table_data=report_table.objects.filter(sl_no=request.GET['key'])
When you set a model field to models.ForeignKey(), the Django dynamic API considers it a reference to that entire object, not the primary key. So a more appropriate name for "cn_number" as a foreign key might be "master_table". Then you can filter by a foreign model field using the format model_name__field_name=field_value.
So, for the report_table model:
master_table = models.ForeignKey(Master_table, on_delete=models.CASCADE)
And for the filter function:
report_table_data = report_table.objects.filter(master_table__cn_number=Master_table_data.cn_number))
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.
I have a model like this:
class CreateDeal(models.Model):
name = models.CharField(max_length=100)
fuel = models.CharField(max_length=15)
mileage = models.PositiveIntegerField(db_index=True)
phone_number = models.CharField(max_length=17)
location = models.CharField(max_length=100, db_index=True)
car_picture = models.ImageField(upload_to='car_picture')
description = models.TextField()
price = models.PositiveSmallIntegerField(db_index=True)
available = models.BooleanField(default=True)
created_on = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
and I have a test class to test the model above like this:
class CreateDealTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username='alfa', email='alfa#hotmail.com', password='top_secret'
)
self.deal = CreateDeal.objects.create(
name='deal1', mileage=100, price=25, user=self.user
)
def test_deal_name(self):
deal = CreateDeal.objects.get(name='deal1')
expected_deal_name = f'{self.deal.name}'
self.assertAlmostEqual(expected_deal_name, str(deal))
if I run the test I have:
Ran 1 test in 0.166s
OK
My question is why django don't raise an exception since almost all fields in my model are required. And what I don't understand is if I remove one field of Createdeal in my setUp (like mileage, price, user or name) I have an error.
For instance if I remove mileage, I have this error:
raise utils.IntegrityError(*tuple(e.args))
django.db.utils.IntegrityError: (1048, "Column 'mileage' cannot be null")
Charfield, Imagefield and Textfield can be empty string which is valid at the database level, some of your fields have default values so they will be written if not set so that makes them also valid at the database level.
PositiveIntegerField and Foreign key cannot be set to empty string, just to value or null so they will fail since null=False by default.
The default blank=False option is only applied at the validation level, not at the database level. This means if you call full_clean() on your model, it will raise a ValidationError. But nothing stops you from saving an invalid model (save() does not call full_clean() as explained here).
I'd like to have a reference field (normally it is a foreignkey field)
parent field is the reference field.
Following is the simplified models to show what I'm trying to do.
For a given class Foo, I'd like to create another class FooCopy that can hold many copies of Foo.
(FooCopy.id, FooCopy.user_edit) pair is unique.
class Foo(Base):
parent = models.ForeignKey(
'self',
null=True, blank=True
)
class FooCopy(models.Model):
_id = models.AutoField(primary_key=True)
id = models.IntegerField(blank=True, null=True, db_index=True)
user_edit = models.ForeignKey(settings.AUTH_USER_MODEL)
parent = models.ForeignKey(
'self',
null=True, blank=True,
to_field='id',
db_constraint=False,
)
foo = Foo.objects.create()
foo_data = model_to_dict(foo)
foo_copy1 = Foo.objects.create(user_edit=user1, **foo_data)
foo_copy2 = Foo.objects.create(user_edit=user2, **foo_data)
def model_to_dict(obj, exclude=[]):
data = {}
for f in obj.__class__._meta.get_fields():
if f.name in exclude:
continue
if f.one_to_many:
continue
if isinstance(f, ForeignKey):
field_name = "{}_id".format(f.name)
else:
field_name = f.name
data[field_name] = getattr(obj, field_name)
return data
I'm getting an error saying Foo.id needs to be unique.
(FooCopy.id must set unique=True because it is referenced by a foreign key.)
Is there a relational field I could use to reference another django model instance without the restriction above? (Or could I get away with it somehow?)
All I need is the ability to use foo and foo_id, I don't need the referential integrity of ForeignKey.
-- edit
After reading Daniel Roseman's comments I think I can have
parent_id = models.IntegerField()
#property
def parent(self):
return self._default_manager.get(id=parent_id, user_edit=self.user_edit)
Although I will probably miss some of stuff that django provides for foreignkey such as parent__name etc, I'm not sure if there's a better way of doing this.