Django : Foreign key set not found after saving object - python

I've got a question model and mcq choices model which have foreign key to question.
class Question(models.Model):
statement = models.TextField(max_length=1024)
def save(self, *args, **kwargs):
super(Question,self).save(*args,**kwargs)
#ques = Question.objects.get(id = self.id)
f = open('/tmp/prj/log.txt', 'w')
choiceobjs = self.choice_set.all()
if choiceobjs:
f.write("choices found")
else:
f.write("choices not found.. zilch")
f.close()
class Choice(models.Model):
value = models.TextField(max_length=1024)
question = models.ForeignKey(Question)
Now I've overridden the save method of question. Even after the question has been saved, I cannot find choice_set in save method! I always get "choices not found.. zilch" in my logfile.
UPDATE: I'm creating my Question in Admin interface, and 'Choice' objects are being created 'inline'.
So the modified question is - In what sequence do the 'inline' fields/models and the main model get created? How can I delay my check for foreignkey set in save method, such that 'foreignkey_set' becomes visible?

class Foo(models.Model):
pass
class Bar(models.Model):
foo = models.ForeignKey(Foo)
When using inlines of Bar in the Foo admin, Django has to save the Foo object first, because the Bar objects need the primary key to reference it in a ForeignKey:
self.save_model(request, new_object, form, change=False)
form.save_m2m()
for formset in formsets:
self.save_formset(request, form, formset, change=False)
http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py#L870
That means when Foo's save method is called, the inline Bar objects haven't been saved yet, and therefore can't be queried. So you need to work around this, if you need to access these objects when a Foo instance was saved in the admin (using Bar inlines).
One possible solution would be to attach to a post_save signal of Bar, see which Foo object it is referencing, and executing the relevant code. But this would trigger on every change, even if no Foo object was created.

Related

Message Object has no attribute 'fields' when updating model field across apps

I have two apps menu and table. In app table, I have this model:
class Table(models.Model):
available = models.BooleanField(verbose_name="Availability", default=True)
def set_availability(self, avail=False):
self.fields['available'] = avail
self.save()
def __str__(self):
return "Table " + str(self.id_num)
In one of the views of app menu, I have the following call:
from table.models import Table
def menu_category_view(request, table_pk):
table = Table.objects.get(pk=table_pk)
if table.available:
table.set_availability(False)
...
return render(request,
...)
When my template calls this view, I receive this error message 'Table' object has no attribute 'fields'. Here, I am trying to update the value of field available of the instance being called (from True to False). And I got this implementation suggested from a book. Is this the right way to update model instance field value? Thanks.
Just set the attribute.
def set_availability(self, avail=False):
self.available = avail
self.save()
Though, it's questionable whether or not set_<field> methods like this are particularly useful. You could work with the object almost as easily:
if table.available:
table.available = False
table.save()

How should i auto fill a field and make it readonly in django?

I`m new to django and i was doing a test for my knowledge.
Found a lot of duplicates in here and web but nothing useful
I'm trying to make a ForeignKey field which gets filled due to the other fields that user fills, and make it unchangeable for the user.
I thought that I should use overriding save() method but couldn't figure that at all.
How should I do that auto-fill and read-only thing?
Your approach is right. Override the save method and if self.pk is not None raise an exception if your field has changed. You can use django model utils to easily track changes in your model: https://django-model-utils.readthedocs.io/en/latest/utilities.html#field-tracker
Principle:
class MyModel(models.Model):
#....
some_field = models.Foreignkey(...)
tracker = FieldTracker()
def save(*args, **kwargs):
if self.pk is None:
# new object is being created
self.some_field = SomeForeignKeyObject
else:
if self.tracker.has_changed("some_field"):
raise Exception("Change is not allowed")
super().save(*args, **kwargs)

Django - dynamic model parameter value

I have two simple models with ForeignKey relation, Category and Object let say (Object has FK attribute to Category) and in administration I need to assign value of another object attribute value base on the actually selected Category.
Example:
I will create in Django Admin interface Category with attribute cat_name="A" and another Category with cat_name="B".
Now in the Object creation form I can select in the form "A" or "B" Category, and based on that selection I need to store in Object.description attribute something like "Selected category is B"
I've tried several approaches but all ended on the fact that the instance of the Category object has to be somehow passed to the Object creation form.
Thanks
You do this before saving you data by overriding the save function:
class ObjectModel(models.Model):
category = models.ForeignKey(# details goes here)
..... # other fields goes here
def save(self, *args, **kwargs):
if self.category.name == 'A':
self.description = ...
elif self.category.name == 'B':
.... # different behavior etc
super(ObjectModel, self).save(*args, **kwargs)
OK, finaly I have find acceptable solution. I'm creating the related object at the time of Category object is being saved and passing it's attribute to the object. Something like
class Category(models.Model):
name = models.CharField(max_length=10)
...
def save(self, *args, **kwargs):
super(Category, self).save(*args, **kwargs)
Object.objects.create(name=self.name)
Only disadvantage I've noticed that Objects created this automated way has to have empty (if allowed) or machine generated attributed, but that's just a minor defect for me and I can do any updates via common administration form is needed.

Creating a display-only (non-editable) Django admin field

Is it possible to build a custom model field/widget combination which displays a value but never writes anything back to the database? I would use this widget exclusively in the admin's forms.
I wrote my own field, which overwrites the formfield() method to declare its own widget class. It displays just fine, but as soon as the 'Save' button is clicked in the admin, I'm getting a validation error:
This field is required.
That makes sense, considering that my widget didn't render out a form field. However, what I'd like to do is basically remove this field from the update process: whenever used in the admin, it just shouldn't be mentioned in the SQL UPDATE at all.
Is that possible?
Here's a sketch of the code I have so far:
class MyWidget(Widget):
def render(self, name, value, attrs=None):
if value is None:
value = ""
else:
# pretty print the contents of value here
return '<table>' + ''.join(rows) + '</table>'
class MyField(JSONField):
def __init__(self, *args, **kwargs):
kwargs['null'] = False
kwargs['default'] = list
super(MyField, self).__init__(*args, **kwargs)
def formfield(self, **kwargs):
defaults = {
'form_class': JSONFormField,
'widget': MyWidget,
}
defaults.update(**kwargs)
return super(MyField, self).formfield(**defaults)
UPDATE 1: The use case is that the field represents an audit log. Internally, it will be written to regularly. The admin however never needs to write to it, it only has to render it out in a very readable format.
I'm not using any other ModelForms in the application, so the admin is the only form-user. I don't want to implement the behavior on the admin classes themselves, because this field will be reused across various models and is always supposed to behave the same way.
There are multiple ways to create a read-only field in the admin pages. Your requirements on the database storage are a bit fuzzy so I go through the options.
You have to register an AdminModel first in admin.py:
from django.contrib import admin
from yourapp.models import YourModel
class YourAdmin(admin.ModelAdmin):
pass
admin.site.register(YourModel, YourAdmin)
Now you can add different behavior to it. For example you can add the list of fields shown in the edit/add page:
class YourAdmin(admin.ModelAdmin):
fields = ['field1', 'field2']
This can be names of the model fields, model properties or model methods. Methods are displayed read-only.
If you want to have one field read-only explicitly add this:
class YourAdmin(admin.ModelAdmin):
fields = ['field1', 'field2']
readonly_fields = ['field2']
Then you have the option to overwrite the display of the field completely by adding a method with the same name. You will not even need a model field/method with that name, then:
class YourAdmin(admin.ModelAdmin):
fields = ['field1', 'field2']
readonly_fields = ['field2']
def field2(self, obj):
return '*** CLASSIFIED *** {}'.format(obj.field2)
With django.utils.safestring.mark_safe you can return HTML code as well.
All other options of the Admin are available, except the widget configuration as it applies to the writable fields only.
I might be a little confused as to what you want but you might want to look into model properties. Here is an example for my current project.
Code inside your model:
class Textbook(models.Model):
#other fields
#property
def NumWishes(self):
return self.wishlist_set.count()
Then you can just display it on the admin page.
class Textbook_table(admin.ModelAdmin):
fields = ["""attributes that are saved in the model"""]
list_display = ("""attributes that are saved in the model""", 'NumWishes'')
So now I can display NumWishes in the admin page but it doesn't need to be created with the model.
Hello in the class admin modify the permission method
#admin.register(my_model)
class My_modelAdmin(admin.ModelAdmin):
def has_delete_permission(self, request, obj=None):
return False
def has_change_permission(self, request, obj=None):
return False

When is the formfield() method called in Django?

This is a question on making custom fields in Django. I'm making a field called EditAreaField, which inherits from TextField. Here's what my code looks like:
class EditAreaField(models.TextField):
description = "A field for editing the HTML of a page"
def formfield(self, **kwargs):
defaults = {}
defaults['widget'] = EditArea() # setting a new widget
defaults.update(kwargs)
return super(EditAreaField, self).formfield(**defaults)
On the 5th line, I'm assigning a custom widget to this field. On line 6, I update the parameters.
The problem is, Django sends a parameter widget that's set to django.contrib.admin.widgets.AdminTextareaWidget, which overrides my EditArea() widget.
How can I change the value that Django is setting? Obviously I could just override their setting by switching lines 5 and 6, so my code looks like:
defaults.update(kwargs)
defaults['widget'] = EditArea() # override django here
But is that really the best way to do it?
As a side note, I couldn't find documentation on the formfield() function anywhere on Django's site: is it deprecated?
It looks like the formfield method is called by the ModelForm helper. According to the docs, the formfield method should include only a form_class attribute to point to the formfield class for this custom model field. This is a custom (or default) form field class, which is where the default widget is defined
from myapp.forms import MyCustomFormField
#create a custom model field
class EditAreaField(models.TextField):
def formfield(self, **kwargs):
defaults={'form_class': MyCustomFormField}#pass our custom field as form_class
defaults.update(kwargs)
return super(EditAreaField, self).formfield(**defaults)

Categories