As you can tell, i'm new to Django, but already love it. I have a functional scraping server that scrapes 7 selects from a diagnostics page on one server. All environments I want to use this will have many of these servers with the same data to monitor performance. I would like to use this function to scrape this data from all entered target servers by input from the html template. Adding each server to monitor that will show up below the input text field from the main html page. I have completed this with a static url, but have been unsuccessful passing the different urls to scrape from the html template to the views url variable I have for the static address.
I've attempted to create forms and pass that to the html template without success, including editing the views file. Reverted the code back to the original working code to not cause more confusion.
html template:
<form method="POST">
{% csrf_token %}
<div class="field has-addons">
<div class="control is-expanded">
<input type="text" class="input"
placeholder="Relay Name">
</div>
<div class="control">
<button type="submit" class="button is-info">
Add Relay
</button>
</div>
</div>
</form>
Views.py:
import requests, bs4
from django.shortcuts import render
from django.http import HttpResponse
from bs4 import BeautifulSoup
from urllib.request import urlopen
from .models import Relay
def index(request):
url = 'hardcoded server url'
page = urlopen(url)
soup = BeautifulSoup(page, 'html.parser')
relay = 'Relay'
dic = requests.get(url.format(relay))
elema = soup.select('body > div:nth-child(13) > div.forminput')
elem1 = elema[0].text.strip()
elemb = soup.select('body > div:nth-child(14) > div.forminput')
elem2 = elemb[0].text.strip()
elemc = soup.select('body > div:nth-child(15) > div.forminput')
elem3 = elemc[0].text.strip()
elemd = soup.select('body > div:nth-child(16) > div.forminput')
elem4 = elemd[0].text.strip()
eleme = soup.select('body > div:nth-child(17) > div.forminput')
elem5 = eleme[0].text.strip()
elemf = soup.select('body > div:nth-child(18) > div.forminput')
elem6 = elemf[0].text.strip()
elemg = soup.select('body > div.versioninfo')
elem7 = elemg[0].text.strip()
#creating dictionary object
dic = {}
dic['relay'] = relay
dic['FFSL'] = elem1
dic['FFCL'] = elem2
dic['FBFQFSL'] = elem3
dic['FBQFCL'] = elem4
dic['TQQ'] = elem5
dic['SQQ'] = elem6
dic['RV'] = elem7
print(dic)
context = {'dic' : dic}
return render(request, 'relchchk/relchck.html', context)
forms.py:
from django import forms
from django.forms import ModelForm, TextInput
from .models import Relay
class RelayForm(ModelForm):
class Meta:
model = Relay
fields = ['Name', 'Relay Version', ]
widgets = {'name' : TextInput(attrs={'class' : 'input',
'placeholder' : 'url'})}
models.py:
from django.db import models
class Relay(models.Model):
name = models.CharField(max_length=45)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'Relays'
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index),
]
The desired result would be to manually enter any of the target servers that could accumulate to all and save in database that exists (but not important now) and have the main page show all selected. I was moving along pretty well and thought this should be simple step and probably is, but I must be missing something. Any guidance would be much appreciated.
You haven't created an instance of your form. In your views.py file, it should look something like this.
if request.method="POST":
#code to handle the form
else:
form = RelayForm()
context = {
"form": form
}
You render the form like this:
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
</form>
Read the docs to learn more about form handling and rendering.
Related
I am trying to make a Django ModelForm that retrieves data from my database using the GET method. When I click the submit button nothing happens. What am I doing wrong?
HTML doc
<form role="form" action="" method="GET" id="form-map" class="form-map form-search">
<h2>Search Properties</h2>
{% csrf_token %}
{{ form.as_p }}
<input type="submit" action= "" class="btn btn-default" value="Submit">
<input type="reset" class="btn btn-default" value="Reset">
</form><!-- /#form-map -->
forms.py
from django import forms
from .models import StLouisCitySale208
from django.forms import ModelForm, ModelMultipleChoiceField
class StLouisCitySale208Form(ModelForm):
required_css_class = 'form-group'
landuse = forms.ModelMultipleChoiceField(label='Land use', widget=forms.SelectMultiple, queryset=StLouisCitySale208.objects.values_list('landuse', flat=True).distinct())
neighborho =forms.ModelMultipleChoiceField(label='Neighborhood',widget=forms.SelectMultiple, queryset=StLouisCitySale208.objects.values_list('neighborho', flat=True).distinct())
policedist = forms.ModelMultipleChoiceField(label='Police district',widget=forms.SelectMultiple,queryset=StLouisCitySale208.objects.values_list('policedist', flat=True).distinct())
class Meta:
model = StLouisCitySale208
fields = ['landuse', 'neighborho', 'policedist', 'precinct20','vacantland', 'ward20', 'zip', 'zoning','asmtimprov', 'asmtland', 'asmttotal', 'frontage', 'landarea','numbldgs', 'numunits']
views.py
from django.views.generic import FormView, TemplateView
from .forms import StLouisCitySale208Form
class StLouisCitySale208View(FormView):
form_class = StLouisCitySale208Form
template_name = 'maps/StlouiscitySale208.html'
maps/urls.py
from django.urls import path
from .views import StLouisCitySale208View, ComingSoonView
app_name = 'maps'
urlpatterns = [
path("maps/stlouiscitysale208",StLouisCitySale208View.as_view(),name="stlouiscitysale208"),
path('maps/coming_soon', ComingSoonView.as_view(), name="coming_soon")
]
You need a get method in your class to tell the button what to do.
class MyView(View):
def get(self, request):
# <view logic>
return HttpResponse('result')
https://docs.djangoproject.com/en/4.0/topics/class-based-views/intro/
Your form currently has method="GET" which is something you'd use for search, or some other operation which doesn't change the state of the application.
If that's what you want to do, and you're using existing data, through a form, to allow users to query the database, then you'll need to implement a get method on the view in order to implement the logic for this. The following should help with the get() method for FormView;
http://ccbv.co.uk/projects/Django/4.0/django.views.generic.edit/FormView/#get
It sounds like you're hoping to create objects using your model form, so change that to method="POST" and you'll at least allow the application to create your object. There may be more to debug at that point, but you need to start by sending data to the server.
Recently I've decide to create form creation form in Flask web app. After searching form creation found Formfield, FieldList classes in flask wtf forms and I can create the form with these classes. but it doesn't provide that I want to.
First- I am going to create a form creation form which will be help me to create form and fields on management interface.
Second- I want to be able to add the fields, not the same type of field, all different kind, such as (booleanField, StringField, IntegerField, DateTimeField etc.) because, in the form there could be different type of fields for specific reason.
Third- I want to retreive this form whenever I want to use in my view
On the DB models side;
class Form(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.StringField)
fields = db.relationship('FormFields', backref='forms', lazy=True)
class FormFields(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.StringField)
field_type = db.Column(db.StringField)
form_id = db.Column(db.Integer, db.ForeignKey('forms.id'), nullable=False)
And othr tables for StrinField, BooleanField, TextField, etc. etc. should be connected this field model, and when I save the data over this created form, these data should be saved int the correct tables
The reason I am searching this, because I don't want to hardcode the Forms and fields in the code, when I need to new form or field I don't want to update code itself, it should be dynamically updated on the database.
And I want to use sqlalchemy based form creation from management page. And this will help to create anytime new form and relate the fields to the form. And on the internet still I didn't find the these style form creation for Flask, almost all of them creating dynamic for with same type of fields
Any ideas?
Last a couple of weeks I was search how to create dynamically flask form based on models
And #nick-shebanov has been redirect me to another approach EAV impelemntation, which is really diffucult to implement. I've tried :)
And decide to create form based on dictionary, and intend to populate the related attributes from the model and pass it to form as dictionary.
What I've done so far;
# app.py file
from flask_wtf import Form
from flask import Flask, render_template, request, flash
from flask_wtf import FlaskForm
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from wtforms import TextField, IntegerField, HiddenField, StringField, TextAreaField, SubmitField, RadioField,SelectField
from wtforms import validators, ValidationError
app = Flask(__name__)
app.secret_key = 'secret123'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///sqlite.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
migrate = Migrate(app, db)
# form class with static fields
class DynamicForm(FlaskForm):
form_type = HiddenField(default='FormType', render_kw={ 'type':'hidden' })
# name = StringField()
#app.route('/', methods=['GET', 'POST'])
def index():
fields = {
'username': 'Username',
'first_name': 'Fisrt Name',
'last_name': 'Last Name',
'email': 'Email',
'mobile_phone': 'Mobile Phone'
}
for key, value in fields.items():
setattr(DynamicForm, key, StringField(value))
form = DynamicForm()
if request.method == 'POST':
# print(dir(request.form))
# print(request.form)
dict = request.form.to_dict()
# print(dict.keys())
print(request.form.to_dict())
return render_template('index.html', form=form)
if __name__ == '__main__':
app.run(debug = True)
# index.html template
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form method="POST">
{{ form.csrf_token }}
{{ form.form_type }}
{% for field in form if field.name != 'csrf_token' %}
{% if field.name != 'form_type' %}
<div>
{{ field.label() }}
{{ field() }}
{% for error in field.errors %}
<div class="error">{{ error }}</div>
{% endfor %}
</div>
{% endif %}
{% endfor %}
<input type="submit" value="Go">
</form>
</body>
</html>
Now I can see my all fields has been rendered including hidden field. When I fill the form and post the data, I can capture it.
But still I didn't achieve to implement save the captured data into database like vertical DB modelling style yet
Here is my simple approach of DB modelling
see image here
Is there suggestions?
I`ve spent a few hours trying to debug this issue. I am trying to import a catalog of product into my app. I am using Django Import-export to import a csv file. I have tried to import it via the admin site and via a simple upload file. In both case, I am encountering some errors. Would you have an idea how to resolve them?
1st Method: Import the csv file via the admin site
Error encountered: Imported file has a wrong encoding: 'charmap' codec can't decode byte 0x9d in position 13365: character maps to
It looks like this method cannot accept some character(""). How can I change my code to accept any character? I`ve already removed the following characters: £, - and try to encode it in UTF-8. I have also done a search to remove every non ASCII characters(128-255)
2eme Method: Import the csv file via the website
Error encountered: 'MultiValueDict' object is not callable
views.py
def simple_upload(request):
if request.method == 'POST':
file_format = request.POST.get('file-format')
product_resource = ProductResource()
dataset = Dataset()
new_products = request.FILES['Price_List']
if file_format == 'CSV':
imported_data = dataset.load(new_products.read().decode('utf-8'),format='csv')
result = product_resource.import_data(dataset, dry_run=True)
elif file_format == 'JSON':
imported_data = dataset.load(new_products.read().decode('utf-8'),format='json')
# Testing data import
result = product_resource.import_data(dataset, dry_run=True)
if not result.has_errors():
# Import now
product_resource.import_data(dataset, dry_run=False)
return render(request, 'catalog/simple_upload.html')
models.py
from import_export import resources
from .models import Product
class ProductResource(resources.ModelResource):
class Meta:
model = Product
skip_unchanged = True
report_skipped = False
simple_upload.html
{% extends 'base.html' %}
{% block body %}
<h3>Import Data</h3>
<p>importing to database</p>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="importData">
<p>Please select format of file.</p>
<select name="file-format" class="form-control my-3">
<option selected>Choose format...</option>
<option>CSV</option>
<option>JSON</option>
</select>
<button class="btn btn-primary" type="submit">Import</button>
</form>
{% endblock %}
Many Thanks,
So I want to link to a given page that has filters on it, and have it display every item in the table before I click search, and only stop displaying items when bad input is given. My problem is similar to the following problem, with a few differences. Empty result list on django-filter page startup
The differences being the poster's default behavior is my desired behaviour and I am using class based views, not functional views.
My urls:
from django.urls import path
from . import views
app_name = 'advising'
urlpatterns = [
path('', views.MyList.as_view(), name='MyList'),
]
my views:
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import generic
from django.template import loader
from .models import *
from django_filters.views import FilterView
from .filter import *
class MyList(FilterView):
template_name = 'advising/MyList.html'
context_object_name = 'tables'
filterset_class = MyFilter
def get_queryset(self):
return Table.objects.order_by('Name')
my filter:
import django_filters
from .models import Table
class MyFilter(django_filters.FilterSet):
Name = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Table #The table this form will reference
fields = ["Name"]
my template:
<form method="get">
{{ filter.form.as_p }}
<button type="submit">Search</button>
</form>
{% if tables %}
<ul>
{% for table in tables %}
<li>{{table}}</a></li>
{% endfor %}
</ul>
{% else %}
<p>Nothing to see here!.</p>
{% endif %}
Is there any way to mimic the behavior of searching for an empty string when the page first loads?
To be very specific, I want the url advising/ to have the same behaviour as the url advising/?Name=
Right now advising/ always gives me an empty list
Finally found a post with the same issue I'm having (no idea why it was never coming up on Google) where the issue was solved. It's as simple as adding the line "strict = False" in my view.
This is the question I found that answered it for me:
Display all record on django-filter page startup
Python 3.5.1
Django 1.10
Been tearing my hair out for hours on this, but have my Reverse Argument error pinned down to the actual problem.
When I try to open a form to edit a particular record in my model, it only opens a blank (unconnected) form. Using the same logic, I am able to delete a record, so I'm sure this is something stupid-simple. But I'm too many hours into this, so I would appreciate a lifeline.
From models.py
class CmsSpaces(models.Model):
sid = models.AutoField(db_column='SID', primary_key=True)
section = models.CharField(db_column='Section', max_length=5)
...Many more columns...
def __unicode__(self):
return self.name
def get_absolute_url(self):
return reverse('cms_spaces:space_edit', args = (self.sid), kwargs=None)
return reverse('cms_spaces:space_delete', args = (self.sid), kwargs=None)
return reverse('cms_spaces:space_new', args = None, kwargs = None)
class Meta:
managed = False
db_table = 'cms_spaces'
From views.py
def CmsSpaces_update(request, sid,
template_name='space_edit.html'):
space = get_object_or_404(CmsSpaces, sid=sid)
form = space_form(request.POST or None, instance=space)
if form.is_valid():
form.save()
return redirect('space_list')
return render(request, template_name, {'form':space_form})
def CmsSpaces_delete(request, sid,
template_name='space_delete.html'):
space = get_object_or_404(CmsSpaces, sid=sid)
if request.method=='POST':
space.delete()
return redirect('space_list')
return render(request, template_name, {'object':CmsSpaces})
From urls.py
from django.conf import settings
from django.conf.urls import include, url
from django.contrib import admin
from cms_spaces import views
urlpatterns = [
url(r'^space_list/$', views.CmsSpaces_list, name='space_list'),
url(r'^space_new/$', views.CmsSpaces_create, name='space_new'),
url(r'^space_edit/(?P<sid>[\w-]+)/$', views.CmsSpaces_update, name='space_edit'),
url(r'^space_delete/(?P<sid>[\w-]+)/$', views.CmsSpaces_delete, name='space_delete'),
]
From space_edit.html. When I enter the url directly for .../space_delete/12345, it does proceed to delete the record with sid=12345.
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Add or Update Space" />
</form>
From space_list.html (which throws "Reverse for 'space_edit' with arguments '(10256,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []") as an error. 10256 is in fact an sid for one of the records in the table. When I remove the links to space_edit and space_delete, it does render the list of records. Yes, there are many more lines of code that handle presentation, but this is the boiled down version, and it is "broken" even at this level.
<ul>
{% for CmsSpaces in space_list %}
<li>{{CmsSpaces.sid}} {{ CmsSpaces.section }} {{ CmsSpaces.space}} {{ CmsSpaces.lot }} {{ CmsSpaces.deednum}}
edit
delete
</li>
{% endfor %}
</ul>
All I want to do is be able to call and edit a record. If I can do that, I believe the list view will work, and then I'm well on my way to home free. The issue seems to be that even though it is correctly capturing the sid, it is failing to correctly pass it as an argument to the model. Help! Thanks.
EDIT
from django import forms
from django.forms import ModelForm
from masterdata.models import CmsSpaces
class space_form(ModelForm):
class Meta:
model = CmsSpaces
fields = [
'sid',
'section',
'space',
'lot',
'deednum',
'purchdate',
'notes',
'perpetualcare',
]
Okay... first off a big thanks to #knbk for getting me on track with the list issue.
Second, my initial diagnosis in the OP was wrong. The issue was in my CmsSpaces_update view. Boiled down, the view had the pieces to tie the form to the record, but no actual code to do so. slaps head
So because someone else may someday read this and wonder the same thing...
def CmsSpaces_update(request, sid,
template_name='templates/space_edit.html'):
space = get_object_or_404(CmsSpaces, sid=sid)
form = space_form(request.POST or None, instance=space)
if form.is_valid():
form.save()
return redirect('space_list')
ctx = {}
ctx['form'] = form
ctx['space'] = space
return render(request, template_name, ctx)