I am trying to create a Django web app that accepts text in a form/textbox, processes it and redirects to a webpage showing the processed text . I have written a half-functioning app and find de-bugging quite challenging because I don't understand most of what I've done. I'm hoping you will help me understand a few concepts, Linking to resources, also appreciated.
Consider this simple model:
class ThanksModel(models.Model):
thanks_text = models.CharField(max_length=200)
Is the only way to set the text of thanks_text through the manage.py shell? This feels like a pain if I just have one piece of text that I want to display. If I want to display a webpage that just says 'hi', do I still need to create a model?
Consider the view and template below:
views.py
class TestView(generic.FormView):
template_name = 'vader/test.html'
form_class = TestForm
success_url = '/thanks/'
test.html
<form action = "{% url 'vader:thanks'%}" method="post">
{% csrf_token %}
{{ form }}
<input type = "submit" value = "Submit">
</form>
I need to create another model, view and html template and update urls.py for '/thanks/' in order for the success_url to redirect correctly? (That's what I've done.) Do I need to use reverse() or reverse_lazy() the success_url in this situation?
Models are used when you are dealing with Objects and Data and DataBases that can contain a lot of information.
For Example A Person would be a model. their attributes would be age, name, nationality etc.
models.py
class Person(models.Model):
Name = models.CharField(max_length=50)
age = models.IntegerField()
nationality = models.CharField(max_length=50)
Thi deals with multiple bits of information for one object. (the object being the person)
A Thank you message would not need this? so scrap the model for the thank you message. just have views where you create the view using a templates and setting the view to a url.
views.py
class TestView(generic.FormView):
template_name = 'vader/test.html' # self explantory
form_class = TestForm # grabs the test form object
success_url = reverse_lazy('vader:thanks') # this makes sure you can use the name of the url instead of the path
def ThanksView(request): # its simple so you don't even need a class base view. a function view will do just fine.
return render(request,"thanks.html")
test.html
<form action = "{% url 'vader:thanks'%}" method="post">
{% csrf_token %}
{{ form }}
<input type = "submit" value = "Submit">
</form>
thanks.html
<h1>Thank you for Submitting</h1>
<h2> Come Again </h2>
url.py
from django.urls import path
from djangoapp5 import views
urlpatterns = [
path('', TestView.as_view(), name='test_form'),
path('thanks/', views.ThanksView, name='vader:thanks'),
]
I haven't tested this but hopefully it helps and guide you in the right direction
Related
Setup
I have successfully setup a CreateView class that allows user to create a Journal. You only have to enter the name and press the "Add" button.
views.py
...
class CreateToJournal(LoginRequiredMixin, CreateView):
model = to_journal
template_name = 'to_journals/to_journal_list.html'
fields = ('journal_name',)
def get_success_url(self):
return reverse('to-journals')
def form_valid(self, form):
form.instance.journal_user = self.request.user
return super(CreateToJournal, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(CreateToJournal, self).get_context_data(**kwargs)
context['to_journals'] = to_journal.objects.filter(journal_user=self.request.user)
return context
...
to_journal_list.html
...
<form class="" id="myForm" action="" method="post">
{% csrf_token %}
{{ form }}
<button type="submit" id="add-button">Add</button>
</form>
...
This creates a new Journal and reloads the page to reflect the changes made.
I am now trying to move the whole Journal creation process to the "home" page, using a template tag. Mostly it works fine and renders all user journals on the home page, but there is a problem with form rendering.
Problem
The new template tag does not render the form created by CreateView. The input field is not displayed here on the page with a template tag.
I couldn't find any specific information (or general, for that matter) regarding the relationship between forms and templatetags.
My setup for the template tag is the following:
...
├───pages
│ └───templatetags
│ └───__init__.py
│ └───journal_tags.py
...
├───templates
│ └───template_tags
│ └───journal_list.html
...
Where pages is the app for "static" pages.
journal_tags.py
from django import template
from to_journals.models import to_journal
register = template.Library()
#register.inclusion_tag('template_tags/journal_list.html', takes_context=True)
def get_journals(context):
journals = to_journal.objects.filter(journal_user=context['request'].user)
return {'journals': journals}
journal_list.html
...
<form class="" id="myForm" action="" method="post">
{% csrf_token %}
{{ form }}
<button type="submit" id="add-button">Add</button>
</form>
...
home.html
...
{% load journal_tags %}
{% get_journals %}
...
Thanks for taking the time to look at this question. Any help is appreciated.
If more information is needed I will happily provide.
Edit
I know realize that I have no reference to the view class created class CreateToJournal(LoginRequiredMixin, CreateView) in my template tag at all. I couldn't find any resource online that would help me figure it out. How can achieve this? I hope my explanations are celar. Thanks in advance.
Edit 2
Sorry for not being clear. Here is a little more in depth more clear explanation. Thanks.
Currently, my view class CreateToJournal(LoginRequiredMixin, CreateView) is rendered at http://127.0.0.1:8000/journals/ and looks like this:
Here you enter the name and press Add. Page reloads and the new journal is added to the list.
I want to achieve the same thing on http://127.0.0.1:8000/. I thought the best way to achieve this is with a templatetag. Below is the image of my current result. I am now able to pull all the journals on the home page, but the only thing that is not showing is the input field for the journal.
Ultimately, I want to have a few forms shoing on the home page in the future. I thought that templatetags is a good and sustainable way to do that.
Edit 3
I am now adding the actual view to the journal_tags.py
from django import template
from to_journals.models import to_journal
from to_journals.views import CreateToJournal
register = template.Library()
#register.inclusion_tag('template_tags/journal_list.html', takes_context=True)
def get_journals(context):
journals = to_journal.objects.filter(journal_user=context['request'].user)
return {'journals': journals,
'form': CreateToJournal(), #new
}
The output is not the actual form, rather the type of the object.
models.py
likes = models.IntegerField()
forms.py
class LikeForm(forms.ModelForm):
class Meta:
model = Post
fields = ("likes",)
How could I make a button that add 1 to this IntegerField every time it's clicked? The default value is 0.
I'm guessing I have to use "submit" button but I'm not sure how I can do that with out rendering the form on the page.
In your views.py you could add something like this:
def record_like_view(request, pk):
if request.method == 'POST':
post = Post.objects.get(pk=pk)
post.likes += 1
post.save()
...
Then in your template:
<form method="post">
{% csrf_token %}
<a class="btn" href="{% url 'my_app:record_like' post.id %}">Like</a>
</form>
You are just posting to a URL. Even though you are still using a form in your template, there is no need for the LikeForm in this case. Take a look at this Django tutorial for another example.
As far as a user preventing a user from clicking multiple times (as pointed out by guillermo) this would require something a little more complicated.
I have an HTML page with a link to redirect to another HTML template which has a form. I have used the class based view in rendering it. But it simply does not load.
My views.py look something like :
def startup(request):
return render(request, 'main/startup.html')
class AddStartup(CreateView):
model = Startup
template_name = 'startup_form.html'
fields = ['startup_name', 'startup_product', 'startup_date', 'startup_sector', 'startup_team_size',
'startup_desc', 'startup_team_condition', 'startup_team']
urls.py
# I have posted only the relevant code
url(r'startup/$', views.startup, name = 'startup'),
url(r'startup/add-startup/$', views.AddStartup.as_view(), name = 'add-startup'),
My HTML page which has a link to navigate to the field is below
{% extends "main/base.html" %}
{%block content%}
pass<br>
<a
href = "{%url "main:add-startup"%}" target="_parent" method = "post">
Add Startup
</a>
{%endblock%}
I am a bit confused in Class based views so that is why I choose to mix them. Help would be appriciated
I read the following thread: Django Multiple Choice Field / Checkbox Select Multiple
But I somehow miss something important as I can't succeed in displaying the checkboxes in my template. However, the name of the field does appear in the template but that's all, after the field name, it's all white and blank.
Curiously, in the thread I read, the author didn't wrote a list of tuple. That's why I think the problem could lie in the models.py
Here is my models.py
from django.db import models
from user.models import User
class RegionChoices(models.Model):
REGION_CHOICES = (
('London', 'Londres'),
('Paris', 'Paris'),
('Berlin', 'Berlin'),
)
region = models.CharField(max_length=30, choices=REGION_CHOICES)
def __str__(self):
return self.region
class Offer(models.Model):
publisher = models.ForeignKey(User)
content = models.TextField()
region_choices = models.ManyToManyField(RegionChoices)
def __str__(self):
return self.publisher.username
forms.py
from django import forms
from django.contrib import admin
from django.conf import settings
from offers.models import Offer, RegionChoices
class SendOfferForm(forms.ModelForm):
region_choices = forms.ModelMultipleChoiceField(queryset=RegionChoices.objects.all(), widget=forms.CheckboxSelectMultiple)
class Meta:
model = Offer
exclude = ['publisher']
offer.html
<form action="{% url "send_offer" %}" method='POST' class='sendofferform'>
{{ form.errors }}
{{ form.non_field_errors }}
{% csrf_token %}
{{ offerform.as_p }}
</form>
views.py
if offerform.is_valid():
sent = True
offer = offerform.save(commit=False)
offer.publisher = User.objects.get(id=logged_user.id)
offer.save()
offerform.save_m2m()
else:
print(offerform.errors)
From your code sounds like you want to limit the choices of region your project can have. I think you should create an admin for RegionChoices first. In there you could create entrances of RegionChoices you like. Follow the django docs if you are not sure how to create an admin interface for a model https://docs.djangoproject.com/en/1.8/ref/contrib/admin/
ps: You might want to do unique=True on region field in RegionChoices. Otherwise you might create duplicate entries of the same region by accident.
Okay, I realize I had to load data in the model RegionChoices.
I loaded the data in the admin part of my website and now, it works perfectly.
I'm learning Django Framework, and I have a question. To help you understand I will try and explain using the example below:
Suppose that we have some table in db as is:
CREATE TABLE names (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100));
And I have the form in Django Admin as is:
<form>
<textarea name="names"></textarea>
<input type="submit" name="sbt" value="Submit">
</form>
User entered something in the input names in the form and submitted it. Then a script catches this data and splits it into an array (str.split("\n")) and in cycle adding to table names!
And I many quetion:
How i can add form to Django Admin?
How i can catch form data and add this data to somethink table in database?
Thanks.
First of all you must create a django model.
Put this code in models.py.
class Names(models.Model):
name = models.CharField(max_length = 100)
Then you must create the admin model.
Put this code in admin.py.
class NamesAdmin(admin.ModelAdmin):
list_display = ['name']
# whatever you want in your admin panel like filter, search and ...
admin.site.register(Names, NamesAdmin)
I think it meet your request. And for split the names you can override save model method and split the names in there. But if you want to have an extra form, you can easily create a django model form.
Put the code somewhere like admin.py, views.py or forms.py
class NamesForm(forms.ModelForm)
class Meta:
model = Names
That's your model and form. So, if your want to add the form to django admin panel you must create a view for it in django admin. For do this create a view as common.
Put the code in your admin.py or views.py.
def spliter(req):
if req.method == 'POST':
form = NamesForm(req.POST)
if form.is_valid():
for name in form.cleaned_data['names'].split(' '):
Names(name = name).save()
return HttpResponseRedirect('') # wherever you want to redirect
return render(req, 'names.html', {'form': form})
return render(req, 'names.html', {'form': NamesForm()})
Be aware you must create the names.html and put the below code in you html page.
{% extends 'admin/base_site.html' %}
{% block content %}
<!-- /admin/names/spliter/ is your url in admin panel (you can change it whatever you want) -->
<form action="/admin/names/spliter/" method="post" >{% csrf_token %}
{{ form }}
<input type="submit" value="'Send'" >
</form>
{% endblock %}
This is your view and your can use it everywhere. But if you want only the admin have permission to see this page you must add this method too your NamesAdmin class.
def get_urls(self):
return patterns(
'',
(r'^spliter/$', self.admin_site.admin_view(spliter)) # spliter is your view
) + super(NamesAdmin, self).get_urls()
That's It. I hope this can help you.