django - creating a preview page - django

i have a 'Add Post' page in my blog app. and i wanna add a 'preview page'. i should click preview button even before save the post. for this ; i ve created two new fields in my models.py which are : titlePreview and bodyPreview.
what i am doin is saving form datas into these two fields to preview the page before publish.
but i couldn't do that. i have a addPost view. here it is :
#login_required(login_url='/panel')
def addpost(request):
if request.method=="POST":
form = addForm(request.POST)
if form.is_valid():
titleform=form.cleaned_data['title']
bodyform=form.cleaned_data['body']
checkform=form.cleaned_data['isdraft']
owner = request.user
n = Post(title = titleform, body = bodyform, isdraft=checkform, owner=owner)
n.save()
return HttpResponseRedirect('/admin/')
else:
form=addForm()
return render(request,'add.html',{'form':form,})
return render(request,'add.html',{'form':form,})
here is my models.py:
class Post(models.Model):
owner = models.ForeignKey(User)
title = models.CharField(max_length = 100)
body = models.TextField()
bodyPreview = models.TextField()
titlePreview = models.CharField(max_length=100)
slug = AutoSlugField(populate_from='title',unique=True)
posted = models.DateField(auto_now_add=True)
isdraft = models.BooleanField(default=False)
def __unicode__(self):
return self.title
#permalink
def get_absolute_url(self):
return ('view_blog_post',None, {'postslug':self.slug})
now i think i should create a different view like 'preview_view' which must redirects to 'preview.html' from addpost.html.
but i couldnt connect the points.
thank you

Explanation of my previous comments
First part: Creating page template passes the blog post data via POST
This part can be implemented in many ways, this is one with a form:
<form action={% url "preview_page" %} method="POST">
<input type="hidden" name="title" value="{{ post_title }}">
<input type="hidden" name="body" value="{{ post_body }}">
<input type="submit" name="preview_button" value="preview">
</form>
Second part: Create the view preview_page
#login_required(login_url='/panel')
def previewpost(request):
if request.method=="POST":
# Here pass the POST data to the template, POST is already a dict,
# so it may be can fit directly into the template if names match
# though it is always better to check the values first
return render(request,'post_template.html', request.POST)
else:
...
post_template.html
...
<h1>{{ title }}</h1>
<p>{{ body }}</p>
...
Disclaimer: The code is not tested at all
The downside of this solution is that you have to actually pass the data (title, body) again from the client to the server. I hope this works for you.

Related

How to update a specific manytomany field in django

My Area model has an exercise attribute with a ManyToManyField to my Exercise model:
class Area(models.Model):
name = models.CharField(max_length=100)
exercise = models.ManyToManyField(Exercise)
class Exercise(models.Model):
name = models.CharField(max_length=100)
My AreaView displays a list of areas, which each link to their own list of specific exercises, shown by AreaDetailView:
class AreaView(ListView):
model = Area
template_name = 'workouts/areas.html'
class AreaDetailView(DetailView):
model = Area
template_name = 'workouts/exercises.html'
def get_context_data(self, **kwargs):
context = super(AreaDetailView, self).get_context_data(**kwargs)
context['e_form'] = AddExerciseForm
return context
e.g:
areas.html
- abs
- biceps
- cardio
- legs ...
exercises.html
Abs
- Ab-wheel
- Cable-crunch
- Plank ...
Biceps
- Barbell curl
- Cable curl
- Dumbbell curl
AreaDetailView also displays a form which I would like to allow the user to create their own exercises, which will be specific to their corresponding area.
Here is my form:
class AddExerciseForm(forms.ModelForm):
class Meta:
model = Exercise
fields = ['name']
My template:
<form action="{% url 'exercise_add_new' %}" method="post">
{% csrf_token %}
{{ e_form }}
<button type="submit">Save changes</button>
</form>
My url:
path('exercise-add-new', ExerciseFormView.as_view(), name='exercise_add_new'),
And here is my CreateView which is supposed to handle the logic:
class ExerciseFormView(CreateView):
form_class = AddExerciseForm
success_url = '/'
def form_valid(self, form):
form.save()
new_ex = Exercise.objects.latest('id')
area = Area.objects.get(id=1)
area.exercise.add(new_ex)
return super(ExerciseFormView, self).form_valid(form)
This allows me to update the first object in my Area model ok, but I need to adjust the value of the variable area in form_valid so that the current 'id' is updated. For example if I click on 'Biceps' and then complete the form, I want to add an exercise related to 'id=2'
I have tried area = Area.objects.get(id=self.kwargs['id'])and other similar variations but so far nothing I have tried has worked
In ExerciseFormView are you trying to add a new exercise to an area or create a new area?
If adding a new exercise you will have to pass the area-id from the URL something like add_exercise/<area_id>, if doing the latter it should be straightforward.
You have pass area-id in URL you can do like below
path('exercise-add-new/<int:area_id>/', ExerciseFormView.as_view(),
name='exercise_add_new')
Then update your view as below
def form_valid(self, form):
form.save()
area = Area.objects.get(pk=self.kwargs["area_id"])
new_ex = Exercise.objects.latest('id')
area.exercise.add(new_ex)
return super(ExerciseFormView, self).form_valid(form)
Also update template as :
<form method="post">
{% csrf_token %}
{{ e_form }}
<button type="submit">Save changes</button>
</form>

Passing data to form (from POST and url)

I'm not sure how to pass data from a given object (profile) and from form (request.POST) to one model (many to many relation).
I have two models: Profile and Keyword. Users can define many profiles (for different subjects) and in each profile they can define many keywords (used later by a crawler). It is possible that the same keyword is in many profiles, and one profile can have many keywords.
Now, I have a view adding new profile to user, and in next step I want to add view adding keyword/keywords to this particular profile.
I'm passing a parameter foreign key - profile_id - via url, and I have build form from my model Keyword. Now I have problem with passing both of them to my function.
models.py
class Profiles (models.Model):
id_profile = models.AutoField(primary_key=True)
user_id = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=45)
description = models.TextField(max_length=120, default=' ')
class Keywords (models.Model):
id_keyword = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
id_profile = models.ManyToManyField(Profiles)
template
<button class="btn btn-outline-danger" type="submit"> new keyword </button>
urls.py
path('profile_detail/<int:pk>/', users_view.ProfileDetailView.as_view(), name = 'profile_detail'),
path('new_keyword/<profile_id>/', users_view.NewKeyword, name = 'new_keyword'),
views.py
def newKeyword(request):
form = NewKeyword(request.POST)
if request.method == 'POST':
form.is_valid()
form.save()
return redirect('profile')
return render(request, 'users/new_keyword.html', {'form': form})
Now I have
__init__() got an unexpected keyword argument 'profile_id'
I understand that I have to somehow overwrite init() to accept profile_id, but I'm not sure how.
ok, thank you for you answer. I have changed my code, but now I have different problem:The Keywords could not be created because the data didn't validate.
def newKeyword(request):
context = {}
context['id_profile'] = request.POST.get('id_profile', None)
form = NewKeyword(request.POST or None)
if request.method == 'POST':
form.is_valid()
obj = form.save(commit=False)
obj.save()
obj.id_profile.add(context['id_profile'])
obj.save()
return redirect('profile')
return render(request, 'users/new_keyword.html', {'form': form})
and template in previous page:
<form method="POST" name="newkeyword" value='keyword'>
<fieldset class="form-group">
<legend class="border-bottom mb-4"> New keyword </legend>
{{ form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-danger" type="submit">create</button>
</div>
</form>
Looks like after clicking a button on page profile (in order to go to view: creating keyword) I use method POST to send parameter, hence my 'If statement' runs...
You are trying to retrieve GET data.
def newKeyword(request, profile_id):
and you can retrieve that parameter with this way but for POST data,
you can use;
request.GET.get('profile_id') # GET method
request.POST.get('profile_id') # POST method
and you don't need to edit url for this.
related question:
Passing variable from django template to view

The form editing the existing form does not save and redirects to wrong. Django

I'm trying to add an edit form to an existing model, but it does not save every time and redirects me to the home page instead of the 'account' page. What am I doing wrong? why changes in the existing model are not visible? any help will be appreciated.
views.py
def account(request):
data_now = datetime.datetime.now().strftime("%Y-%m-%d")
#my form
time = get_object_or_404(Time, pk=52)
if request.method == "POST":
form = TimeEditForm(request.POST, instance=time)
if form.is_valid():
time = form.save(commit=False)
time.save()
return redirect('account')
else:
form = TimeEditForm(instance=time)
context = {'data_now': data_now, 'time_edit_form': form}
return render(request, 'account.html', context)
forms.py
class TimeEditForm(forms.ModelForm):
class Meta:
model = Time
fields = ('compartment',)
labels ={
'free_or_no': 'field name in my language?'
}
models.py
class Time(models.Model):
day_time = models.ForeignKey(DayTime, on_delete=models.CASCADE)
compartment = models.CharField(max_length=11)
free_or_no = models.BooleanField(default=True)
time_equivalent = models.IntegerField()
urls.py
urlpatterns = [
url(r'^$', views.masseur_detail, name='masseur_detail'),
url(r'^account$', views.account, name='account')
]
account.html
<form action="." method="post">
{% csrf_token %}
{{ time_edit_form|crispy }}
<button type="submit" class="btn btn-block btn-primary"> Save</button>
</form>
This is quite a subtle issue.
Usually in Django it's recommended to use URLs that end with a slash - eg "/account/" - but your URL is just "/account", without the slash.
Now, when you put action="." in your form, the browser interprets this as "post to the root of the current directory". If your URL did end with a slash, that would be resolve to the same page. But because it doesn't, the browser posts to the root itself, ie "/".
The best solution is to change your URL pattern to r'^account/$'; alternatively (but not recommended) you could change your form to use action="".

quick fill forms in django

Sorry I have a really basic question while learning Django and could not find an easy answer.
My model is :
class Entry(models.Model):
name = models.CharField(max_length=200)
type = models.CharField(max_length= 200)
date = models.DateTimeField(auto_now= False, auto_now_add=True)
updated = models.DateTimeField(auto_now= True, auto_now_add= False)
description = models.TextField()
And so my general form implementation is :
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['name','type', 'description']
views:
def add(request):
if request.method == 'POST':
form = EntryForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
return HttpResponseRedirect('/')
else:
form = EntryForm()
return render(request, "form.html", {'form': form})
I want to add a quick fill button next to add button (that calls above view ) where the name and type is statically filled in the object and only textbox appears for description field.
I could not find a way to statically assign the values to my field in Django.
I had tried creating a different HTML file ( quickform.html) but {{form.as_p}} will put all the fields.
my forms.html is
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button class="btn btn-success" type='submit'>Submit</button>
</form>
what would be the best way to add a quick link to my index page where the name ( is auto-filled to the "general"+str(id)) and type is auto-filled to "general") is auto-filled and does not appear in the form page
In my opinion the easiest way to do such a thing is to add on click event to your second button then write a simple javascript function the fills the elements you want with static values.

Django and users entering data

I am building a webapp which will be used by a company to carry out their daily operations. Things like sending invoices, tracking accounts receivable, tracking inventory (and therefore products). I have several models set up in my various apps to handle the different parts of the web-app. I will also be setting up permissions so that managers can edit more fields than, say, an office assistant.
This brings me to my question. How can I show all fields of a model and have some that can be edited and some that cannot be edited, and still save the model instance?
For example, I have a systems model for tracking systems (we install irrigation systems). The system ID is the primary key, and it is important for the user to see. However, they cannot change that ID since it would mess things up. Now, I have a view for displaying my models via a form using the "form.as_table". This is efficient, but merely spits out all the model fields with input fields filling in the values stored for that model instance. This includes the systemID field which should not be editable.
Because I don't want the user to edit the systemID field, I tried making it just a label within the html form, but django complains. Here's some code:
my model (not all of it, but some of it):
class System(models.Model):
systemID = models.CharField(max_length=10, primary_key=True, verbose_name = 'System ID')
systemOwner = models.ForeignKey (System_Owner)
installDate = models.DateField()
projectManager = models.ForeignKey(Employee, blank=True, null=True)
#more fields....
Then, my view for a specific model instance:
def system_details(request, systemID):
if request.method == 'POST':
sysEdit = System.objects.get(pk=systemID)
form = System_Form(request.POST, instance=sysEdit)
if form.is_valid():
form.save()
return HttpResponseRedirect('/systems/')
else:
sysView = System.objects.get(pk=systemID)
form = System_Form(instance=sysView)
return render_to_response('pages/systems/system_details.html', {'form': form}, context_instance=RequestContext(request))
Now the html page which displays the form:
<form action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Save Changes">
<input type="button" value="Cancel Changes" onclick="window.location.href='/systems/'">
</form>
So, what I am thinking of doing is having two functions for the html. One is a form for displaying only those fields the user can edit, and the other is for just displaying the content of the field (the systemID). Then, in the view, when I want to save the changes the user made, I would do:
sysValues = System.objects.get(pk=SystemID)
form.save(commit = false)
form.pk = sysValues.sysValues.pk (or whatever the code is to assign the sysValues.pk to form.pk)
Is there an easier way to do this or would this be the best?
Thanks
One thing you can do is exclude the field you don't need in your form:
class System_Form(forms.ModelForm):
class Meta:
exclude = ('systemID',)
The other is to use read-only fields: http://docs.djangoproject.com/en/1.3/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields as #DTing suggessted
To make a field read only you can set the widget readonly attribute to True.
using your example:
class System_Form(ModelForm):
def __init__(self, *args, **kwargs):
super(System_Form, self).__init__(*args, **kwargs)
self.fields['systemID'].widget.attrs['readonly'] = True
class Meta:
model = System
or exclude the fields using exclude or fields in the class Meta of your form and display it in your template if desired like so:
forms.py
class System_Form(ModelForms):
class Meta:
model = System
exclude = ('systemID',)
views.py
def some_view(request, system_id):
system = System.objects.get(pk=system_id)
if request.method == 'POST':
form = System_Form(request.POST, instance=system)
if form.is_valid():
form.save()
return HttpResponse('Success')
else:
form = System_Form(instance=system)
context = { 'system':system,
'form':form, }
return render_to_response('some_template.html', context,
context_instance=RequestContext(request))
some_template.html
<p>make changes for {{ system }} with ID {{ system.systemID }}</p>
<form method='post'>
{{ form.as_p }}
<input type='submit' value='Submit'>
</form>