how to create a django app that performs a google search - python

I want to make a Django app that searches on google a string and then saves the html page.
so far I managed to
create this
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<div class="container text-center">
<h1>my search engine</h1>
<h3>Pressing search will run a google search for 'lallero search', then the code will scrape the first 5 links in that google search and store the corresponding html.</h3>
About page
<br>
<br>
<form action="{% url 'search' %}">
<input type='submit' value='search' class="btn btn-primary">
</form>
</div>
that searches on google a very specific string and it is just a press button.
I would like to add a form where I can write the string I want to search and then pass it as input to my "search" function
so far I came out with this
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<div class="container text-center">
<h1>my search engine</h1>
About page
<br>
<br>
<form action="{% url 'search' %}">
<input type="search" value='lallero search' class="form-control rounded" placeholder="Search" aria-label="Search"
aria-describedby="search-addon" />
<button type="button" class="btn btn-outline-primary">search</button>
</div>
but I do not know how to pass the string I write as input to the function.
any advice?
--------------------update
Following comments, I put the SearchForm into a utils file that I import and then I changed my search function to
def search(request):
form = SearchForm(request.GET)
if form.is_valid(): # this will validate your form
search_text = form.cleaned_data["search"] # now you can access input
urls = searchWeb(num=5, stop=5, query_string=search_text)
threads = [threading.Thread(target=getSavePage, args=(url,)) for url in urls]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
return JsonResponse(urls, safe=False)
I changed my template to
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<div class="container text-center">
<h1>my search engine</h1>
About page
<br>
<br>
<form action="{% url 'search' %}">
{{ form.non_field_errors }}
{{ form.as_p }} <!-- This will create a text input with attributes -->
<input type="submit" value="Submit">
</form>
</div>
I get an error as the form status is unknown

Briefly, in your view that is been invoked by the form can access your form data with request.GET, request.POST. Since its search functionality get method should be used. And your input will be in your querysting something like www.yourdomain.com/search-view?search=input.Instead of accessing it by request.GET["search"], django forms will be a better choice. In your case
from django import forms
class SearchForm(forms.Form):
search = forms.CharField(required=True, max_lenght=255, label="Search")
And you can use it in your template as
<form action="{% url 'search' %}">
{{ form.non_field_errors }}
{{ form.as_p }} <!-- This will create a text input with attributes -->
<input type="submit" value="Submit">
</form>
And you can use it in your view;
from . import SearchForm
form = SearchForm(request.GET)
if form.is_valid(): # this will validate your form
search_text = form.cleaned_data["search"] # now you can access input
Please check out working with forms.
Edit:
def search(request):
if request.method == 'GET':
# create a form instance and populate it with data
form = SearchForm(request.GET)
# check whether it's valid:
if form.is_valid():
print("Form is valid now you can process your data")
# return a response or render a different template
return HttpResponseRedirect('/redirect-url/')
# if not render the form back
return render(request, 'your_template.html', {"form": form})
# Assume post method is not allowed
return HttpResponseNotAllowed()

following #berkeeb answer I changed the code in this way.
I created a forms.py file with:
from django import forms
class SearchForm(forms.Form):
search = forms.CharField(required=True, max_length=255, label="search")
in my template (home.html) I used:
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<div class="container text-center">
<h1>my search engine</h1>
About page
<br>
<br>
<form action="{% url 'search' %}">
<label for="search_text">Your search: </label>
<input id="search_text" type="text" name="search_text">
<input type="submit" value="Search">
</form>
</div>
and finally in the search function I wrote:
def search(request):
form = SearchForm(request.GET)
search_text = form.data["search_text"] # now you can access input
urls = searchWeb(num=5, stop=5, query_string=search_text)
threads = [threading.Thread(target=getSavePage, args=(url,)) for url in urls]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
return render(request, "engine/search.html", {"search": urls})
basically I had to remove the validation part of the form as I kept receiving status unknown.

Related

How do I implement a form submission if the form is in my base template file?

I have a base template file that holds the form for the users to subscribe to the email newsletter. All of my templates inherit from the base template file, as I'd like to display this form on every web page.
I don't know how do I make the form submit the data the user inputs into the database. So far, I dealt with views and each view was specific to a URL, so it's not really obvious to me how do I do this for all URLs, since the base template is present on all URLs.
base.html (the base template file):
{% load static %}
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
Homepage
Post a job
{% block content %}{% endblock %}
<p> Subscribe to new jobs: </p>
<form method="post">
<p> Email: <input type="email" name="email" /> </p>
<p> First name: <input type="text" name="first_name" /> </p>
<p> Last name: <input type="text" name="last_name" /> </p>
<input type="submit" value= "Submit">
</form>
</body>
</html>
I also made a form in my forms.py file which constructs the form from my email subscriber model, but I don't use it anywhere so far:
EmailSubscriberForm = modelform_factory(EmailSubscriber, fields=["email", "first_name", "last_name"])
How do I achieve what I want?
you need to use a ModelForm, that will link a form to a model when calling save() method. (this will add a lot of security too)
from django.forms import ModelForm
from myapp.models import EmailSubscriber
# Create the form class.
class EmailSubscriberForm(ModelForm):
# if email is an EmailField, `is_valid` method will check if it's an email
class Meta:
model = EmailSubscriber
fields = ["email", "first_name", "last_name"]
Then in the view you can create and pass as a context or get the response and save to the database
if request.method == "POST":
form = EmailSubscriberForm(request.POST)
if form.is_valid():
email_subscriber = form.save()
# generally call `return HttpResponseRedirect` here
else:
form = EmailSubscriberForm()
# generally call `return render(request, 'page.html', {'form': form})
and you just call this in your template :
<form method="post">
{{ form }}
</form>
Ref : https://docs.djangoproject.com/en/3.1/topics/forms/modelforms/

Django forms interacting with each other

I have a django (2.0) application with a page that contains two separate forms that are not meant to interact with each other at all. However, I am getting some weird behavior. Pictured here, when I click on one of the labels on the bottom form, it triggers the checkbox for the corresponding (based on row, not name) element in the upper form (clicking "members" on the group form selected "Email" on the person form). This only goes one way--clicking anywhere on the top form never effects the bottom form. Checking the actual boxes of the bottom form do trigger the expected boxes that their labels correspond to.
The html for this page is:
<head>
{% load static %}
<title>LDAP - Search</title>
<link rel="shortcut icon" href="{% static 'djangoWrapper/favicon.ico' %}">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{% static 'ldap/style.css' %}">
</head>
<body>
<h1>Search</h1>
<div class="search-form">
<h2>Search for Person</h2>
<form action="{% url 'ldap:person_results' %}" method='post'>
{% csrf_token %}
<table>
{{ personForm }}
</table>
<input type="submit" value="Search">
</form>
</div>
<div class="search-form">
<h2>Search for Group</h2>
<form action="{% url 'ldap:group_results' %}" method='post'>
{% csrf_token %}
<table>
{{ groupForm }}
</table>
<input type="submit" value="Search" />
</form>
</div>
<div class="url">
or go back to login
</div>
</body>
And the forms are
class PersonSearchForm(forms.Form):
uniqname = forms.CharField(label="uniqname", max_length=10)
options = personFieldsDict.items()
attributes = forms.MultipleChoiceField(
widget=forms.CheckboxSelectMultiple, choices = options, label='',
required=False)
class GroupSearchForm(forms.Form):
groupName = forms.CharField(label='Group Name')
options = groupFieldsDict.items()
attributes = forms.MultipleChoiceField(
widget=forms.CheckboxSelectMultiple, choices = options, label='',
required=False)
And the view that renders this page is simply:
def search(request):
personForm = PersonSearchForm()
groupForm = GroupSearchForm()
context = {'personForm': personForm, 'groupForm': groupForm}
return render(request, 'ldap/search.html', context)
I am guessing that this has something to do with the fact that both forms use a MultipleChoiceField widget, but I can't see how they are interacting with each other if their attributes are different and they are in different div's. Any idea on why there is this interaction? Thanks.
You should use the prefix argument to one or both forms to prevent the fields from interferin with each other.
personForm = PersonSearchForm(prefix="person")
groupForm = GroupSearchForm(prefix="group")
Don't forget to also use the prefixes when instantiating the form on post.

Flask and WTForms with custom validation

I am creating an online form with Python, to send an online form. The form consists of a mixture of free input fields, and standard options. What it does now is convert the input to a mail, and send it. That's great, but I would like to build in a functionality that checks the input first. I need the length of two different inputfields to be of the size. So if someone enters 4 products and only 3 quantities, I want it to return a warning that these amounts differ.
base.py:
from flask import *
from wtforms import *
import yagmail
yag = yagmail.SMTP('email', 'pass')
# App config.
DEBUG = True
app = Flask(__name__)
app.config.from_object(__name__)
app.config['SECRET_KEY'] = 'key'
class ReusableForm(Form):
naam = TextField('Name:', validators=[validators.required()])
#app.route("/", methods=['GET', 'POST'])
def index():
form = ReusableForm(request.form)
print form.errors
if request.method == 'POST':
# Process all input
naam=request.form['naam']
productcodes=request.form['productcodes']
productquantity=request.form['productquantity']
# Convert SKU & EAN input to list of entries
productcodes = [int(i) for i in productcodes.strip('{}').split('\n')]
productquantity = [int(i) for i in productquantity.strip('{}').split('\n')]
# tried this; didn't work
# if len(productcodes) != len(productquantity):
# flash('Unequal inputs')
if form.validate():
# Comment when form is validates
flash('Order succesvol: ' + naam)
(send mail)
else:
flash('Error: All the form fields are required. ')
return render_template('hello.html', form=form)
if __name__ == "__main__":
app.run()
hello.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Form</title>
<link rel="stylesheet" media="screen" href ="static/css/bootstrap.min.css">
<link rel="stylesheet" href="static/css/bootstrap-theme.min.css">
<meta name="viewport" content = "width=device-width, initial-scale=1.0">
</head>
<body>
<div class="container">
<h2>Form</h2>
<form action="" method="post" role="form">
{{ form.csrf }}
<div class="form-group">
<label for="naam">Naam:</label>
<select name="naam" class="selectpicker form-control">
<option value="Jack">Jack</option>
<option value="John">John</option>
</select>
<br>
<label for="productcodes">SKU-codes:</label>
<textarea class="form-control" id="productcodes" name="productcodes"></textarea>
<br>
<textarea class="form-control" id="productquantity" name="productquantity"></textarea>
<br>
</div>
<button type="submit" class="btn btn-success">Send</button>
</form>
<br>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for message in messages %}
{% if "Error" not in message[1]: %}
<div class="alert alert-info">
<strong>Success! </strong> {{ message[1] }}
</div>
{% endif %}
{% if "Error" in message[1]: %}
<div class="alert alert-warning">
{{ message[1] }}
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
</div>
<br>
</div>
</div>
</body>
</html>
So what i would like to have is that upon the first click, the check is executed. If the size of the input of productcodes is smaller than the size of productquantity, the user has to edit the inputs. If not, the user has to confirm and is able to send the mail.
The lines that are commented out in base.py didn't work.
Thanks in advance!
Quick and Dirty
Based on the commented-out code, in your original attempt there was nothing stopping the form from validating and submitting. Your if len(productcodes) != len(productquantity) just flashed a message, which only shows on the next page load, but doesn't stop validation of the form. form.validate() can only validate the fields you tell it about.
Change the check to be something like:
if len(productcodes) != len(productquantity) and form.validate():
That way your basic check will bounce to the failed validation code path.
The "Right" Way
Keep in mind, even if that works, you're still basing your check on parsing text from a <textarea>, with little to enforce the format. Any unexpected input is likely to break it.
I'd strongly recommend you rethink your form to be a bit more in line with how WTForms and Flask work. Each form field should map directly to an attribute of your Form subclass. That way you can use the built-in validators, and not have to create your own. You should probably look at using something other than a raw <textarea> as well. It may be a bit more work up-front, but it will make it a lot easier for you and your users down the road.

Django search as input is entered into form

I currently have a working search form in my project that passes through form data to the GET request. Pretty standard.
What I'm wanting to do is search as data is entered into the search form, so that results will display in real time with search data. This is much like what Google does with the instant desktop results. Is this something that's possible with Django?
Below is my current (simple) search
#views.py
def ProductView(request):
title = 'Products'
all_products = Product.objects.all().order_by("product_Name")
query = request.GET.get("q")
if query:
products = all_products.filter(
Q(product_Name__contains=query) |
Q(manufacturer__contains=query)
).distinct()
return render(request, 'mycollection/details.html', { 'all_products' : products })
-
<!-- HTML -->
<!-- SEARCH BAR -->
<form class="navbar-form navbar-left" role="search" method="get" action="{% url 'mycollection:products' %}">
<div class="form-group">
<input type="text" class="form-control" name="q" value="{{ request.GET.q }}">
</div>
<button type="submit" class="btn btn-default">Search</button>
</form>
you can save the request.data in to session and if any data is associated with session search data you can put in to value of search box.
request.session['search'] = request.GET.get('q','')
templete :
{% if request.session.search %} {{request.session.search}} {% endif %}

How to redirect django.contrib.auth.views.login after login?

I added django.contrib.auth.views.login everywhere in my webpage, for that I had to load a templatetag (that returns the AuthenticationForm) in my base.html. This templatetags includes the registration/login.html template.
The login is working ok but I want it to redirect the users to the same page they are before login. Now, it redirects me to /wherever_i_am/login wich shows registration/login.html with the 'login ok' or 'login fails' messages but without the rest of base.html.
I have followed django documentation and a few SO questions like this but I cannot redirect correctly. I have modified the next variable but it doesn't seem to work (next={{ request.get_full_path }} redirects me to /wherever_i_am/login ...again)
Have you tried something similar? any ideas?
UPDATE1
Now, the question could be something like: Do I have to declare my own login view if I want to include the login form everywhere in my web page?
Thank you.
Found answer:
Change settings.LOGIN_REDIRECT_URL in your settings.py,
below code is copy from django:
if request.method == "POST":
form = authentication_form(data=request.POST)
if form.is_valid():
# Ensure the user-originating redirection url is safe.
if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = settings.LOGIN_REDIRECT_URL
...
The below allows redirects the user to the page they were attempting to access after they log in, but without having to write a custom view. It contains all the code you need to add to make it work. (As an aside, not all the TEMPLATE_CONTEXT_PROCESSORS are needed, but if you set a value to it explicitly you overwrite the defaults so need to re-add them.)
settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.request",
"django.core.context_processors.static",
)
urls.py
from django.contrib.auth.views import login, logout
...the other imports for your app ...
urlpatterns = patterns('',
(r'^login/$', login, {'template_name':'login.html'} ),
(r'^logout/$', logout,{'template_name':'logout.html'}),
...the other urls for your app...
)
login.html
<html>
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
{{form}}<br/>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</html>
logout.html
<html>
<p>You are logged out. To log in again, click here.</p>
</html>
views.py
#login_required(login_url="/login/")
def view1(request):
....this is a view you want to protect with security...
#login_required(login_url="/login/")
def view1(request):
....this is a view you want to protect with security...
I used something like this with default login view:
{% if form.errors %}
<p class="error">Sorry, that's not a valid username or password</p>
{% endif %}
<form action="{% url login %}" method="post">
{% csrf_token%}
<label for="username">User name:</label>
<input type="text" name="username" value="" id="username">
<label for="password">Password:</label>
<input type="password" name="password" value="" id="password">
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ request.get_full_path }}" />
</form>
# or if it's not declareв шт urls:
<form action="{% url django.contrib.auth.views.login %}?next={{ request.get_full_path }}" method="post">
everything worked fine.
PS: are you absolutely sure that "context_processors.request" is included in settings? Forgetting to include it is a common problem.
UPD: As far as I know, there are no way to make default login view to redirect on failed login (It just doesn't work that way).
Still i may be wrong
Finally I created a login view that calls django.contrib.auth.views.login internally.
I'd suggest to pass a previous url as a parameter within the url:
/accounts/login/?next=my_previous_url
and then use this value in a view
request.next
{{request.get_full_path}} gives you the current path, so is normal that the redirect points to the same place, change it for {{next}} in your registration/login.html template
Adding up to #Sean's anwer. Code for iterating over each form field in order to write field error above the miss-typed field.
So, in Sean's login.html is the existing code:
login.html
<html>
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
{{form}}<br/> <!-- I can change! -->
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</html>
Now what you should do is replace the "I can change!" line (4th line in the above code snippet) with following code:
{% for field in form %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<span class="text-danger small"> {{ field.errors }}</span>
</div>
<label class="control-label col-sm-2">{{ field.label_tag }}</label>
<div class="col-sm-10"> {{ field }}</div>
</div>
{% endfor %}
You can use this snippet for other forms too (for example registration). :)
I stumble upon this question in my process of implementing Facebook Account Linking. The problem is the same: how do I correctly redirect django after successful login?
Remember this: your settings.py contain LOGIN_REDIRECT_URL right? So, that's the only place where you should do the logic of redirecting. To do that, first connect this signal (put this in your views.py):
def after_success_login(sender, user, request, **kwargs):
alt = request.GET.get('account_linking_token')
if alt is not None:
uri = request.GET.get('redirect_uri')
request.session['fb_redirect_uri'] = uri
user_logged_in.connect(after_success_login)
The logic above may not reflect your case, but the idea is setting up a session variable to be read in the route defined as LOGIN_REDIRECT_URL.
So, in my case:
def index(request):
if not request.user.is_authenticated():
form = SignUpForm()
return render(request, 'index.html', {'form': form})
else:
# FB ACCOUNT LINKING!
if 'fb_redirect_uri' in request.session:
redirect_uri = request.session['fb_redirect_uri']
del request.session['fb_redirect_uri']
to = '{}&authorization_code={}'.format(redirect_uri, request.user.username)
print('to', to)
return redirect(to)
That's it!
Add a decorator before the view function should be OK.
#login_required
see here for details

Categories

Resources