Editing a Django model with a filefield without re-uploading the file - django-forms

Below is a simplified version of the django code in my project; it allows users to upload a file and give it a title. This feature works perfectly. However, when user goes re-edit the form later, the file and title are redisplayed, but when the user goes to submit the file filed empties. The file field of the form reopened for editing looks like this:
Currently: media_location/uploadedfile.mp3
Change: [Choose File] No file Chosen
And after I submit it, it's:
This Filed is required
[Choose File] No file Chosen
How do I get it so that the user does not have to reupload the file? It does not matter to me whether the field is made readonly once submission is done, or whether it remains editable. The finished project is not for a client and will only be available to a small group of trusted users, but I would still like to follow best practices if possible. Thanks for any help.
Django Code:
models.py
class Recording(models.Model):
rec_title=models.CharField(max_length=200,blank=True)
rec_file = models.FileField(upload_to='recordings')
forms.py
from django import forms
from songstorage.models import Recording
class RecordingForm(forms.ModelForm):
rec_file=forms.FileField(label='Upload File')
rec_title=forms.CharField(label='Recording Title',required=False)
class Meta:
model=Recording
views.py
def addrecordings(request,recordingfile):
#if there is a recordingfile in the url, the user is editing...
if recordingfile:
recording=Recording.objects.get(rec_title=recordingfile)
recording_form = RecordingForm(instance=recording)
#...Otherwise a new form is createing a new one
else:
recording_form = RecordingForm(request.POST, request.FILES)
#When the form is submitted, do the following:
if request.method == 'POST':
#check if its valid
if recording_form.is_valid():
recording_form.save()
#if sucessfully submitted, redirect
return redirect('/recordings/')
return render_to_response('addrecordings.html',{'recording_form':recording_form},context_instance=RequestContext(request))

I had the same problem.
You can override a model's default clean function. This validates all forms, the user can change the image file on edit, and the file is guaranteed to be nonempty.
class MyModel(models.Model):
image = models.ImageField(blank=True)
def clean(self, *args, **kwargs):
super(MyModel, self).clean(*args, **kwargs)
if not self.image:
raise ValidationError('Please provide an image')

I have had the same problem and couldn't figure out how nor able to search anything useful, my current solution is to use another form, in your scenario:
class RecordingUpdateForm(RecordingForm):
rec_file=forms.FileField(label='Upload File', required=False)
The only difference is I am using UpdateView class based view, so you have to modify your view function to use the RecordingUpdateForm for updating.

Related

django-rest-framework: Can Ihave multiple templates per ViewSet?

I've created a serializer and ViewSet for my model and added a template for a list view. To get to see the web page (template) the ordering render classes must be correct and one needs to add the TemplateHTMLRenderer to the list of renderers.
This now leads to the issue that when wanting to browse to a specific record like
/mymodel/5
in the browser, I get shown the list view too.
The goal is to have 1 url that serves the api (json) or the web page both for list and detail views.
(/mymodel = list, /mymodel/5 = detail)
The question is: how can I have multiple templates (list/detail) based on one ViewSet?
The solution is to override the get_template_names method and return the template according to the action being performed.
def get_template_names(self):
if self.action == 'list':
return ['list.html']
elif self.action == 'retrieve':
return ['details.html']

How to get only Tweet (only posts) from the timeline?

Whenever tweet is created, it's activity is added to getStream production app.
class Tweet(models.Model, Activity):
user = models.ForeignKey()
text = models.CharField()
class Follow(models.Model, Activity): <- This is adding new activity to the timeline
def follow_feed_lisnner(~)
signal.post_save.connect(~)
class Like(models.Model, Activity): <- Like is adding to activity so timeline automatically shows who liked this post,
My Expectation:
Feed: only shows Tweet on timeline (I don't want to see who started to follow me, or liked any post) - Just Like Instagram!
Notification: Who started to follow me, Who liked my post, Who commented on my post.
views.py
feeds = feed_manager.get_news_feeds(request.user.id)
# get the newsfeed for user.
activities = feeds.get('timeline').get()['results']
activities = enricher.enrich_activities(activities)
Possible Solutions
Use python-stream (more low level) to deal with this problem. (I don't know if it helps)
Maybe I'm missing a cool feature of stream-django
How can we get only Tweet (Not Like, Follow or other activities which should be in notification) on the timeline?
Thank you
UPDATE
If I understood correctly, this should work. Is this valid?
class Follow(models.Model, Activity):
follower =
following
#property
def activity_author_feed(self):
return 'notification'
Activity 1: user A follows user B.
Activity 1 goes to 'user' feed + 'notification' feed (not timeline feed)
//notification feed name already exists so I don't need to create follow feed group
Activity 2: user B creates Post
Activity 2 goes to 'user' feed + 'timeline' feed
Note: I'm assuming your Follow and Like models have a "user" field. If not, best update the question with the full Model classes and also confirm if you're setting up any other following relationships.
The stream-django integration provides an 'Activity' model Mixin and the FeedManager model Manager. They work together to add activities to a Feed Group and Feed whose unique "feed id" is derived from the Model instance.
By default, the feed id is determined by the application wide settings.USER_FEED setting. That should work well for your Tweet model but is probably not what you want for the Follow and Like models. The activities associated with those models ideally belong in separate feeds. This can be setup by overriding the Activity.activity_author_feed property function.
class Follow(models.Model, Activity):
# snipping fields
#property
def activity_author_feed(self):
return 'Follow' # Must match a Feed Group defined in the Stream dashboard
#property
def activity_actor_attr(self):
return self.author
To have to those activities copied into the notification feed, implement the Activity.activity_notify() function to return a list of target feeds.
#property
def activity_notify(self):
return [feed_manager.get_notification_feed(self.user.id)]

Django autocomplete Light in list filters for admin

I have successfully setup the Autocomplete Registry and have my django admin forms where if you go to the form, the auto completes works. I would like to be able to extend the autocompletes to work on the list_filter view as well. So when you are looking at the view generated by Admin.py -- that the list_filter inputs that are generated would also use the autocomplete jquery + service URL.
I didn't see anything listed in the documentation, anyone have any pointers?
If you are using Django version greater then 2.0, you can try using the built-in autocomplete fields for this purpose.
By default, the admin uses a select-box interface () for those fields. Sometimes you don’t want to incur the overhead of selecting all the related instances to display in the dropdown.
The Select2 input looks similar to the default input but comes with a search feature that loads the options asynchronously
There is a simple app which does this:
To install use: pip install django-admin-autocomplete-filter
Then add admin_auto_filters to your INSTALLED_APPS inside settings.py of your project.
Let's say we have following models:
class Artist(models.Model):
name = models.CharField(max_length=128)
class Album(models.Model):
name = models.CharField(max_length=64)
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
cover = models.CharField(max_length=256, null=True, default=None)
And you would like to filter results in Album Admin on the basis of artist, then you can define search fields in Artist and then define filter as:
from admin_auto_filters.filters import AutocompleteFilter
class ArtistFilter(AutocompleteFilter):
title = 'Artist' # display title
field_name = 'artist' # name of the foreign key field
class ArtistAdmin(admin.ModelAdmin):
search_fields = ['name'] # this is required for django's autocomplete functionality
...
class AlbumAdmin(admin.ModelAdmin):
list_filter = [ArtistFilter]
'''
defining this class is required for AutocompleteFilter
it's a bug and I am working on it.
'''
class Media:
pass
After following these steps you may see the filter as:
You should define your own admin filter that inherits from django.contrib.admin.SimpleListFilter. Then should provide your own HTML template for this filter which will use one of django-autocomplete-light widgets. As a parameter for widget you should define required autocomplete URL. And do not forget to include proper JS and CSS for it.
All of this is done in special app for this: dal-admin-filters

How to populate one dropdown from the other in django dynamically using AJAX/DAJAX/DAJAXICE/Simple Javascript?

I have searched enough of the examples but couldn't get the satisfactory result. Please explain with all the necessary code. I am very poor at AJAX. I tried to use DAJAXICE in my code and got little success but didn't work with passing parameters.
I am using Django 1.6 Dajaxice 0.7 Dajax 0.9.
Any way you feel the easiest is okay but please explain with all the code.
TIA.
If all you need is a simple Django view to fetch some data with AJAX, take a look at django-braces AjaxResponseMixin. Below is a code sample which returns list of objects ids and their names:
from django.views.generic import View
from braces import views
class SomeView(views.JSONResponseMixin, views.AjaxResponseMixin, View):
def get_ajax(self, request, *args, **kwargs):
my_objects = MyObject.objects.all()
json_dict = {
'ids': [x.id for x in my_objects],
'names': [x.name for x in my_objects]
}
return self.render_json_response(json_dict)
Then use jQuery ajax to make the query from your template and populate your fields.
If you're not familiar with Class Based Views, your url for this view could be:
url('^objects/(?P<some_id>[0-9]+)/$', SomeView.as_view())
then in get_ajax you can access self.kwargs['some_id'] to filter objects.

How do I create an empty Django formset using modelformset_factory?

I'm creating a formset, but it seems to populate it with all of the existing data in the table for that object. I can't figure out how to start with a blank formset; the only way seems to be to delete all of the data from the table, but clearly this isn't an option.
I will post code if necessary (but there's lots of it, so knowing what is relevant is tricky).
give a parameter queryset=Model.objects.none() when making the object.
Following Arihant's answer, I did something like this, which works:
class TagCreateFormSet(BaseModelFormSet):
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
queryset=None, **kwargs):
queryset = Tag.objects.none()
super(TagCreateFormSet, self).__init__(data, files,
auto_id, prefix, queryset, **kwargs)
It seems that it isn't possible to change the behaviour of model formset. So as a solution, I changed the data structure so that the data I want to edit is grouped by another type, and then instead I have used inlineformset_factory.

Resources