I am currently running a production Django webapp that holds historic item information using the SimpleHistory feature.
I have a navigation bar that displays all current revisions of all items to click on and view their separate pages. That being said, I wanted to have the ability to select which items to show/hide in the navigation bar by updating a boolean field on the item admin pages.
So, I modified the item models to have a field to do such:
class Item(models.Model)
field1 = models.CharField()
field2 = models.CharField()
...
hide_item = models.BooleanField('Item hidden:', default=True) #don't want history on this field
reason_for_change = models.CharField()
changed_by = models.ForeignKey(User, null=True)
accepted_by = models.ForeignKey(User, null=True)
accepted_date = models.DateTimeField()
history = HistoricalRecords()
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
super(Item, self).save(*args, **kwargs)
#property
def _history_user(self):
return self.changed_by
#_history_user.setter
self.changed_by = value
After making the migrations, this field showed up in the admin pages to my delight, but unfortunately I am not able to modify this field without receiving the following error:
AttributeError: can't set attribute
C:\Python27\lib\site-packages\simple_history\admin.py in save_model, line 151
151. obj._history_user = request.user
I think it might have to do with the fact that all changes to item field need to be tracked using the SimpleHistory feature, but for this particular field I don't want to track and store the history of its changes, I just want to be able to enable and disable at will in the admin page.
I also noticed, that if I make a new instance of an Item on the webapp and check the value of hide_item on the admin item page, it is False when it should be True by default. On the contrary, if I attempt to add a new Item instance within the admin page, hide_item is set to True by default as expected...
Right now I think my best solution might be to make another model that holds hide/display information for all items and keep it separate from the item models.
Wondering if anyone might now how to accomplish this.
Thanks
Might not be the most elegant way to do it, but I ended up making a separate model that stores show/hide information and syncs with the item to be displayed.
I did this using a BooleanField for the show/hide and a readonly OneToOne(Item) field to sync with the item I want to display.
Worked well.
Related
I'm making a simple website using django.
I've added a 'Comment' model to make a comment section on a blog post. I'd like to print out each of the 'date_added', 'text', and 'owner' attributes in html.
class User_Comment(models.Model):
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.text
I have problems with the 'owner' attribute.
owner = models.ForeignKey(User, on_delete=models.CASCADE)
if I try to make migrations with it, Django asks me to provide a default value.
It is impossible to change a nullable field 'owner' on user_comment to non-nullable
without providing a default. This is because the database needs something to populate
existing rows.
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for
this column)
2) Ignore for now. Existing rows that contain NULL values will have to be handled
manually, for example with a RunPython or RunSQL operation.
3) Quit and manually define a default value in models.py.
If I add 'blank=True', 'null=True' parameters to the onwer attribute,
the attribute works but it doesn't automatically associate with the owner when adding a comment. So I have to go to the admin to designate the comment manually to its owner.
owner = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
What'd be the best solution for this ? I'd like to print the 'owner' attribute in html automatically without having to handle it manually.. Thank you so much for your time.
It may be helpful to explain exactly what has happened here.
You have added an additional field, owner, to an existing Comment model. Because there were already some existing comments, the migration process (that updates Django's understanding of the model in the database) needs to know what to do with the existing comment records that currently have no owner.
This is a one-time process purely to handle the existing records.
However, when you create a new comment, you'll also need to handle who the owner is so the model field gets filled automatically. Let's say you have a model form that takes in user comments and your view tests if a comment is being posted:
form = CommentForm(request.POST or None)
if request.method == "POST" and form.is_valid:
#create an uncommitted version of the form to add fields to
form_uncommitted = form.save(commit=False)
#here we explicitly assign the owner field to the user that made the request
form_uncommitted.owner = request.user
#then save the form data plus our added data
form_uncommitted.save()
I have 2 models in my Project with Many to Many relationship. On saving model Event, I read from the event_attendees file and add it to the attendees field in the Event. No errors/exceptions shown but attendee is not added to the attendees field. Do I need to save the model again after altering with the attendees field? If so, how to do that (calling save method from add_attendees will cause the program into infinite loop)?
class Attendee(models.Model):
name = models.CharField(max_length=100)
class Event(models.Model):
name = models.CharField(max_length=100)
event_attendees = models.FileField(upload_to='documents/', blank=True)
attendees = models.ManyToManyField(Attendee, blank=True)
def save(self, *args, **kwargs):
super().save()
self.add_attendees()
def add_attendees(self):
with open(self.event_attendees.url[1:]) as csv_file:
# Some code here
for row in csv_reader:
# Some code here
attendee = Attendee(name=name)
attendee.save()
self.attendees.add(attendee)
print(self.attendees.all()) # attendee added
print(attendee.event_attended) # event present with attendee
#Refresh template to check changes -> Changes lost
It's the Attendee object that you haven't saved.
You can shortcut it by using the create method on the m2m field:
for row in csv_reader:
self.attendees.create(name=whatever)
(Note, please don't blindly catch exceptions. Django will already do that and report a useful error page. Only catch the exceptions you are actually going to deal with.)
Apparently, the feature worked when I used non-admin web dashboard. While using by-default-created /admin dashboard, this feature was not working. I am assuming from the results that the admin side code calls different methods while saving the model object even though I have overridden the save method (and hence my save method along with other methods should be called). I will update with more info if I find it.
I have the model below:
class Account(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
class AccountCreate(generics.ListCreateAPIView):
def get_queryset(self):
return Account.objects.filter(owner=self.request.user)
serializer_class = AccountSerializer
I want to create an account of which the owner is the same as the request.user. But when I tried to create a new account, the dashboard displayed all the users. How to make the choices only be the request.user?
As it is shown in the image with the red circles below, the owner could be wz, but I only want it to be victor.
If there is only one choice, why are you making this a field on the form in the first place? If you remove the owner field from the form, and automatically set the user as the owner in the view, wouldn't that be easier for the user, and less work for you? Furthermore, if the whole form is just that one question (and if this question has only one possible answer), wouldn't it be easier to process object creation in the view, when a button is pressed or something?
I am having problems saving a MultipleChoiceField on my test server (with Apache). However, it works on my local machine (django server).
In a form, I have checkboxes that I can check and uncheck. When I click on a save button, the data related to the checkboxes are saved in the database and the form is reloaded and the checkboxes updated.
However, this is how it works in local but not on the test server. On the test server, when I click on the save button, it just reloads the form, nothing is saved, nothing is changed.
Here is the code:
class Participant(models.Model):
databases = models.ManyToManyField(Advertiser, null=True, blank=True, through='ShareDataToBrands')
#property
def share_to_brands_list(self):
brands=[]
for brand in ShareDataToBrands.objects.all():
brands.append((brand.advertiser.id, brand.advertiser.brand_name, brand.share_data))
return brands
class ShareDataToBrandsForm(forms.ModelForm):
class Meta:
model = models.Participant
fields = ('databases', )
databases=forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False)
def save(self, *args, **kwargs):
instance=super(ShareDataToBrandsForm, self).save(commit=False)
#list of brands to share data with
share_list=map(int, self.cleaned_data.get("databases"))
participant=self.instance
for share_data_instance in models.ShareDataToBrands.objects.filter(participant=participant):
if share_data_instance.advertiser.id in share_list:
share_data=True
else:
share_data=False
#update record
share_data_instance.share_data=share_data
share_data_instance.save()
return instance
What could possibly be wrong?
EDIT :
When I check the log, I see that the program never enters in the for loop! However, I have records in the database matching the filter.
I have found the solution, looping through theShareDataToBrands instances related to the participant instead of all the ShareDataToBrands instances.
In the share_to_brands_list property, have changed the following line:
for brand in ShareDataToBrands.objects.all():
with
for brand in ShareDataToBrands.objects.filter(participant=self):
I need a widget which can make a foreignkey readonly and also it should display the value related to that field not the id
suppose
Class A(models.Model):
id=models.AutoField(primary_key=True)
name=models.CharField(max_length=200)
def __unicode__(self):
return self.name
Class B(models.Model):
id=models.AutoField(primary_key=True)
name=models.ForeignKey(A)
description=models.CharField(max_length=200)
now when i make 'name' of class B as readonly then in admin it only displays the id corresponding value of that name in Class A.Is there any widget that can make the field as readonly and also display the value not id
As a workaround you can:
1) Add the field name to raw_id_fields attribute of ModelAdmin and then
2) Disable id input box using javascript (leaving intact the value label).
It will do what you're asking about except for security issue (if someone imitates disabled/deleted input box). That can additionally be dealt with for example in clean_name function of a class inherited from ModelForm.
What if i display the value in the help_text.Means I m showing the value in help_text as well as Id
This can be achievd simply
def get_form(self, request, obj=None):
form = super(BAdmin,self).get_form(request, obj)
link = obj.id
pl=A.objects.get(id=obj.name_id)
help_text1 = "%s"%(pl.name)
form.base_fields['name'].help_text = help_text1
return form
The third workaround is to use Django trunk which adds readonly_fields property to ModelAdmin.
Other alternative is to patch your current version of django with this patch: http://code.djangoproject.com/ticket/342
EDIT: I am using django r12204, because later revisions break django-cms application, which is vital for me. I thought that later revisions of django had this, but I had to patch my django installation to show foreign key values not id's. But it seems that this behaviour still persists in django trunk, so here is the patch: http://dpaste.com/hold/147814/