I'm going through a django tutorial to create a wiki and I'm a little stumped on what's happening in the view below. Specifically, this part:
if form.is_valid():
article = form.save(commit=False)
article.author = request.user
article.save()
msg = "Article saved successfully"
messages.success(request, msg, fail_silently=True)
return redirect(article)
Here are my questions:
what is being instantiating when you write article = form.save(commit=False) and what does the argument, (commit=False) mean?
Where does request.user come from and what does it do?
I could also use an explanation for article.save()
where does messages.success come from?
Sorry for all the questions, but the tutorial is a little sparse on details :(.
Here's the model:
class Article(models.Model):
"""Represents a wiki article"""
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=50, unique=True)
text = models.TextField(help_text="Formatted using ReST")
author = models.ForeignKey(User)
is_published = models.BooleanField(default=False, verbose_name="Publish?")
created_on = models.DateTimeField(auto_now_add=True)
objects = models.Manager()
published = PublishedArticlesManager()
def __unicode__(self):
return self.title
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Article, self).save(*args, **kwargs)
#models.permalink
def get_absolute_url(self):
return ('wiki_article_detail', (), { 'slug': self.slug })
Here's the full view:
#login_required
def add_article(request):
form = ArticleForm(request.POST or None)
if form.is_valid():
article = form.save(commit=False)
article.author = request.user
article.save()
msg = "Article saved successfully"
messages.success(request, msg, fail_silently=True)
return redirect(article)
return render_to_response('wiki/article_form.html',
{ 'form': form },
context_instance=RequestContext(request))
what is being instantiating when you write article =
form.save(commit=False) and what does the argument, (commit=False)
mean?
Saving a modelform inserts/updates the data in the database and returns the model istance (in this case an article instance). A modelform maps a form to a model. However, sometimes you may want to add some extra stuff that does not come directly from the form. So, to prevent two updates, you do not commit to the database by specifying commit=False, the changes will be made to the database when you do a .save() on instance, instead.
Where does request.user come from and what does it do? request.user refers to the currently logged in user (who is making this request).
I could also use an explanation for article.save() - inserts/updates the article fields to the database.
where does messages.success come from? messages framework is just for passing error/success/informative messages using cookies and sessions.
Related
I am trying to accomplish the following in Django:
An app that allows users to create image posts. They can view any post that is in the system and add those posts to their bookmarks. When a user creates a post, the post is automatically added to their bookmarks.
Model
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
post_image = models.ImageField(max_length=255, upload_to='images/content/', blank=False, null=False)
author = models.ForeignKey(User, on_delete=models.PROTECT, related_name='authors')
tags = TaggableManager(through=UUIDTaggedItem, help_text=_('Maximum of 5 tags allowed'))
created = models.DateTimeField(auto_now_add=True)
bookmarks = models.ManyToManyField(User, related_name='bookmarked', blank=True)
bookmark_count = models.IntegerField(default=0)
def get_create_date(self):
return self.created.strftime("%B %Y")
ordering = ['-created']
View
#login_required
def post_add_view(request, *args, **kwargs):
context = {}
user = request.user
if request.method == 'POST':
form = PostAddForm(request.POST, request.FILES)
if form.is_valid():
new_post = form.save(commit=False)
new_post.author = user
new_post.save()
form.save_m2m()
context['form'] = form
messages.add_message(request, messages.SUCCESS, 'Image uploaded successfuly!')
return redirect('library:posts')
context['form'] = form
return render(request, 'library/post_add.html', context)
else:
form = PostAddForm()
context['form'] = form
return render(request, 'library/post_add.html', context)
When a user creates a new post, that post will be associated with an additional attribute in the Post object called 'bookmarks'. The 'bookmarks' attribute has a many-to-many relationship with author and post.
I can easily create the post object, associate it with the author and save it to the db. That part is working fine. The part that I am struggling with is how to handle the 'bookmarks' field. The author id is easy, since I already have that from the session with user = request.user.
Somehow I need to obtain the newly created post id so that I can attach it to the 'bookmarks' field. This where I am stuck. I am new to Django and coding in general.
I have seen some possible ways to handle this, but I am not sure if they are correct or advisable.
Django Signals
I saw a few posts online about using signals.
Django Transactions
This SO post discusses using transactions
Do I need to use one of the above methods or is there another way of accomplishing what I want? Thanks!
If I understand you correctly, all you need to do is add the user to the bookmarks field of the new_post. However, I think the new_post needs an id for this to be possible, although someone may correct me on that. By which I mean, the user needs to be added to the bookmarks after new_post has been saved (that's when new_post is assigned an id).
So something like:
....
if form.is_valid():
new_post = form.save(commit=False)
new_post.author = user
new_post.save()
new_post.bookmarks.add(user)
form.save_m2m()
...
There are many answers concerning this topic. None of them which I read, helped me to find a way out.
Situation: I've a contact model and I wish to assign an email address. They reside in a table with a foreign key.
Now, I'd like to add a button: "add e-mail" which opens a form. As I am in the detail view of the contact, I'd like to hand the id over into the form to either pre- or post-populate the email object (fk to contact) with the contact pk.
The easiest way would from my point of view be to just add the pk to the url and use it in "form_valid".
The view:
class EmailCreateView(LoginRequiredMixin, CreateView):
model = EmailAddress
fields = ('scope', 'email',) # 'contact',
template_name = 'contacts/contact_form.html'
# initial = {'email': 'test'}
def form_valid(self, form):
form.instance.create_by = self.request.user.username
form.instance.update_by = self.request.user.username
# form.instance.contact = Contact.objects.get(self.kwargs['contact_pk']) <- not working
return super().form_valid(form)
The urlpattern:
path('email/add/', EmailCreateView.as_view(), name='add_email'),
The model:
class EmailAddress(CommonInfo):
scope = models.CharField('Bereich', max_length=2, choices=SCOPE_TYPES)
contact = models.ForeignKey(Contact, verbose_name='Kontakt', on_delete=models.CASCADE)
email = models.EmailField('E-Mail')
def __str__(self):
return '{}'.format(self.email)
def get_absolute_url(self):
return reverse('contacts:detail', kwargs={'pk': self.contact_id})
I really have no idea what I'm missing. As this is a standard case in DB development, it ought to be easy. I'm almost certain that I've overlooked something simple. But whenever I try to pass a url parameter over, I run into a NoReverseMatch error.
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.create_by = self.request.user.username
self.object.update_by = self.request.user.username
self.object.save()
return super().form_valid(form)
I'm struggling with such a problem:
I have models:
class ForecastType(models.Model):
client = models.ForeignKey(Client, related_name="weatherforecast_client")
created_by = models.ForeignKey(Forecaster, related_name="weatherforecast_created_by")
modified_by = models.ForeignKey(Forecaster,
related_name="weatherforecast_modified_by",
blank=True,
null=True)
creation_date = models.DateTimeField(auto_now_add=True)
modification_date = models.DateTimeField(auto_now=True)
weather_forecasts = models.ManyToManyField('WeatherForecast')
STATUS_CHOICES = (
("D", "Draft"),
("A", "Active"),
("H", "History"),
)
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default="D")
class OneDayForecast(ForecastType):
def __str__(self):
return f"Prognoza pogody dla: {self.client}, wykonana dnia: {self.creation_date}"
class WeatherForecast(models.Model):
begin_date = models.DateTimeField(blank=True, null=True)
finish_date = models.DateTimeField(blank=True, null=True)
forecast_type = models.ForeignKey('ForecastType', null=True, blank=True)
description = models.TextField(max_length=300, blank=True, null=True)
I also have ModelForm and InlineFormset:
class OneDayForecastForm(ModelForm):
class Meta:
model = OneDayForecast
exclude = ('weather_forecasts',)
WeatherForecastFormset = inlineformset_factory(OneDayForecast, WeatherForecast, exclude=('forecast_type',), extra=2)
and finally an CreateView:
class OneDayForecast(ForecasterRequiredMixin, CreateView):
template_name = "forecaster/one_day.html"
success_url = reverse_lazy("forecaster:dashboard")
model = OneDayForecast
form_class = OneDayForecastForm
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
weather_forecast_form = WeatherForecastFormset()
return self.render_to_response(
self.get_context_data(form=form, weather_forecast_form=weather_forecast_form)
)
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
weather_forecast_form = WeatherForecastFormset(self.request.POST)
if form.is_valid() and weather_forecast_form.is_valid():
return self.form_valid(form, weather_forecast_form)
else:
return self.form_invalid(form, weather_forecast_form)
def form_valid(self, form, weather_forecast_form):
self.object = form.save(commit=False)
for weather_form in weather_forecast_form:
weather_object = weather_form.save()
self.object.weatherforecast_set.add(weather_object)
self.object.save()
form.save_m2m()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, weather_forecast_form):
return self.render_to_response(
self.get_context_data(form=form, weather_forecast_form=weather_forecast_form)
)
After trying to submit my Form with it's InlineFormset I receive this error:
Request Method: POST
Request URL: http://localhost:8000/forecaster/my-clients/6/one_day/
Django Version: 1.11
Exception Type: ValueError
Exception Value:
Unsaved model instance <OneDayForecast: Forecast for: client1> cannot be used in an ORM query.
Problem probably lies in commit=False in form_valid method but I have no clue how to repair it.
Does anyone know to solve this?
Thanks.
Okay, so I think that there are a couple problems here, both in your post and form_valid() methods. I've referred to my own implementations of inline formsets to see what you do differently.
First of all, I believe that the first line of the post method should be self.object = self.get_object().
Second, weather_forecast_form = WeatherForecastFormset(self.request.POST) should be weather_forecast_form = WeatherForecastFormset(self.request.POST, instance=self.object).
Notice the relationship here between the object we get and then using it at the instance in the formset. That's all for the post method.
Now, in my own implementation, I have many formsets, so I loop through each formset as follows (you can use exactly the same code if you put your formset into a list and pass it to form_valid):
def form_valid(self, form, formsets):
self.object = form.save()
for formset in formsets:
formset.instance = self.object
formset.save()
return HttpResponseRedirect(self.get_success_url())
Notice that we fully save the parent form here, including committing it. We then save all formsets. If you wanted to keep your single formset, you can easily change the above code to the following:
def form_valid(self, form, weather_forecast_form):
self.object = form.save()
weather_forecast_form.instance = self.object
weather_forecast_form.save()
return HttpResponseRedirect(self.get_success_url())
The error that you report at the bottom of your question is a direct result of form.save(commit=False). What is happening there is that you are "pretend" saving the parent, and then trying to fully save the children. The database doesn't have record of the parent, so it spits out that error. Committing before saving many to many records is a must (at least in my experience).
I am working in a blog application using django. A user can add posts and edit their own posts. However, I am having trouble with retrieving user's posts. Here is a similar question, but it does not work with my application: Django edit form based on add form? Any help is truly appreciate
Here is my code:
#login_required
def edit(request, id):
if id:
post = get_object_or_404(BlogPost, id=id)
if post.author != request.user:
return render(request, "403.html")
else:
post = BlogPost(author=request.user)
if request.method == "POST":
form = AddPost(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.save()
messages.add_message(request, messages.SUCCESS,
'You have succesfully updated your post')
return redirect('homepage')
else:
form = AddPost(instance=post)
return render(request, 'blog/update.html', {'form': form})
Here is my model:
class BlogPost(models.Model):
title = models.CharField(
max_length=100, null=False, blank=False, unique=True)
content = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
author = models.CharField(max_length=50).......
Your author field is a CharField. I'm not sure what you're putting into that field, or how you're setting it, but it will never equal request.user, because that is an instance of your User model.
Normally the author field would be a ForeignKey to User, so that the comparison would be true for those posts authored by the current user. If you are putting the username into the author field, you could compare with that: if post.author != request.user.username - but I would really not recommend doing that, as you are breaking normalization for no good reason.
(Also note my other comment: your first else clause looks like it should be one indent to the left: ie under if id, not if post.author....)
I have a form that edits an instance of my model. I would like to use the form to pass all the values as hidden with an inital values of username defaulting to the logged in user so that it becomes a subscribe form. The problem is that the normal initial={'field':value} doesn't seem to work for manytomany fields. how do i go about it?
my views.py
#login_required
def event_view(request,eventID):
user = UserProfile.objects.get(pk=request.session['_auth_user_id'])
event = events.objects.get(eventID = eventID)
if request.method == 'POST':
form = eventsSusbcribeForm( request.POST,instance=event)
if form.is_valid():
form.save()
return HttpResponseRedirect('/events/')
else:
form = eventsSusbcribeForm(instance=event)
return render_to_response('event_view.html', {'user':user,'event':event, 'form':form},context_instance = RequestContext( request ))
my forms.py
class eventsSusbcribeForm(forms.ModelForm):
eventposter = forms.ModelChoiceField(queryset=UserProfile.objects.all(), widget=forms.HiddenInput())
details = forms.CharField(widget=forms.Textarea(attrs={'cols':'50', 'rows':'5'}),label='Enter Event Description here')
date = forms.DateField(widget=SelectDateWidget())
class Meta:
model = events
exclude = ('deleted')
def __init__(self, *args, **kwargs):
super(eventsSusbcribeForm, self).__init__(*args, **kwargs)
self.fields['username'].initial = (user.id for user in UserProfile.objects.filter())
my models.py
class events(models.Model):
eventName = models.CharField(max_length=100)
eventID = models.AutoField(primary_key=True)
details = models.TextField()
attendanceFee = models.FloatField(max_length=99)
date = models.DateField()
username = models.ManyToManyField(UserProfile, related_name='user', blank=True)
eventposter = models.ForeignKey(UserProfile, related_name='event_poster')
deleted = models.BooleanField()
def __unicode__(self):
return u'%s' % (self.eventName)
Can you post your Event model? It's too hard to guess what you are trying to do without that. I have to assume a few things without it, so I'm sorry if I'm wrong.
First off, I'm guessing that you should not be using an Event ModelForm for the EventSubscriptionForm. That doesn't really make sense. Hopefully, you created a through class for Event and User, so in your Event model, you have something like
subscriber_users = models.ManyToManyField(User, through="Subscription")
and
class Subscription(models.Model):
user = models.ForeignKey(User, related_name="events",)
event = models.ForeignKey(Event, related_name="subscribers")
Then you can use a Subscription ModelForm.
Is there any reason you're using eventID instead of the django idiom, event_id? You should also import your Event and EventSubcribeForm classes with Pythonic casing. One very important thing is that you should be linking everything to User and not UserProfile.
Technically, it makes more sense to set initial in the view rather than the form init, because you would have to pass request.user to init anyway.
I think you should try this for your view...
#login_required
def event_view(request, event_id=None):
user = request.user.get_profile()
event = Event.objects.get(id=event_id)
initial = {'user': request.user}
form = EventSubcriptionForm(request.POST or None, instance=event, initial=initial)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('event_list'))
return render_to_response('event_view.html', {
'event': event,
'form': form
}, context_instance = RequestContext(request))
A few notes
use request.user.get_profile() for the current user's profile object
you can use request.POST or None to avoid the request.method cases
always use named urls so you can reverse on names instead of hard-coding urls into views
if you want user in your template context, just setup a context processor (see pinax for example on how to do this) instead of passing it in every single view. You can always use request.user also.
Keep in mind that this code will only work if you have that through class setup like I said and you use a form like
class EventSubcriptionForm(forms.ModelForm):
class Meta:
model = Subscription
exclude = ('event')
EDIT
Thanks a bunch for the ups. I'm not new to django, but somehow very new to SO.
Okay, you should really read some of the PEPs about Python conventions http://www.python.org/dev/peps/pep-0008/ or some SO posts about it What is the naming convention in Python for variable and function names?.
Here's what I recommend for your event app models.py:
class Event(models.Model):
name = models.CharField(max_length=100)
details = models.TextField()
attendance_fee = models.FloatField(max_length=99)
date = models.DateField()
poster = models.ForeignKey(User, related_name='events_posted')
deleted = models.BooleanField()
attendee_users = models.ManyToManyField(User, through="Attendance")
def __unicode__(self):
return self.name
class Attendance(models.Model):
user = models.ForeignKey(User, related_name="events",)
event = models.ForeignKey(Event, related_name="attendees")
Notes
The name of a class is capitalized and singular. You are not describing events, you are the blueprint for an Event.
you never need the name of the class in its attributes, i.e. event_name can just be name.
all variables are lowercase_and_underscored
always link to User, not your profile model. A lot of django code expects this.
So now you can access the users attending the event with event.attendees.
I found this while trying to set defaults for the manytomany. I didn't want to add a through table.
based on the view Casey posted, but adding the user in the manytomany relation.
for the initial post:
#login_required
def event_view(request, event_id=None):
user = request.user.get_profile()
event = Event.objects.get(id=event_id)
initial = {'user': request.user, 'username': [ request.user.id, ] } # makes the poster also an attendee
form = EventSubcriptionForm(request.POST or None, instance=event, initial=initial)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('event_list'))
return render_to_response('event_view.html', {
'event': event,
'form': form
}, context_instance = RequestContext(request))
updated version:
#login_required
def event_view(request, event_id=None):
user = request.user.get_profile()
event = Event.objects.get(id=event_id)
initial = {'user': request.user, 'subscriber_users': [ request.user.id, ] } # makes the poster also an subscriber
form = EventSubcriptionForm(request.POST or None, instance=event, initial=initial)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('event_list'))
return render_to_response('event_view.html', {
'event': event,
'form': form
}, context_instance = RequestContext(request))