<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Cibernatural S.L.</title>
	<atom:link href="http://www.cibernatural.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.cibernatural.com</link>
	<description>Páginas web en Lanzarote y Desarrollo de software profesional - Islas Canarias</description>
	<lastBuildDate>Wed, 28 Jul 2010 08:57:15 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="http://superfeedr.com/hubbub"/>		<item>
		<title>Web de Hormicasa en Alemán</title>
		<link>http://www.cibernatural.com/web-de-hormicasa-en-aleman/</link>
		<comments>http://www.cibernatural.com/web-de-hormicasa-en-aleman/#comments</comments>
		<pubDate>Wed, 28 Jul 2010 08:55:42 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Noticias]]></category>
		<category><![CDATA[Arrecife]]></category>
		<category><![CDATA[Hormicasa]]></category>
		<category><![CDATA[Hormiconsa]]></category>
		<category><![CDATA[Inmobiliaria]]></category>
		<category><![CDATA[Lanzarote]]></category>

		<guid isPermaLink="false">http://www.cibernatural.com/?p=670</guid>
		<description><![CDATA[La web de servicios inmobiliarios en Lanzarote http://www.hormicasa.es estrena secciones en alemán. Web de Hormicasa en Alemán Enlaces relacionados Web Corporativa de Hormicasa Servicios Inmobiliarios Blog de Hormicasa Hormicasa y C2 Gestión Inmobiliaria se publicitan mediante Mediasand Descargar en PDF]]></description>
			<content:encoded><![CDATA[<p>La web de servicios inmobiliarios en Lanzarote <a href="http://www.hormicasa.es">http://www.hormicasa.es</a> estrena secciones en alemán.</p>
<p><a href="http://www.hormicasa.es/home2.jsp">Web de Hormicasa en Alemán</a><br />
<br/><br/></p>
<h3>Enlaces relacionados</h3>
<ul>
<li><a href="http://www.cibernatural.com/web-corporativa-de-hormicasa-servicios-inmobiliarios/">Web Corporativa de Hormicasa Servicios Inmobiliarios</a></li>
<li><a href="http://www.cibernatural.com/blog-de-hormicasa/">Blog de Hormicasa</a></li>
<li><a href="http://www.cibernatural.com/hormicasa-y-c2-gestion-inmobiliaria-se-publicitan-mediante-mediasand/">Hormicasa y C2 Gestión Inmobiliaria se publicitan mediante Mediasand</a></li>
</ul>

                            <div id="aspdf">
                                <a href="http://www.cibernatural.com/wp-content/plugins/as-pdf/generate.php?post=670">
                                    <span>Descargar en PDF</span>
                                </a>
                            </div>
                        ]]></content:encoded>
			<wfw:commentRss>http://www.cibernatural.com/web-de-hormicasa-en-aleman/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Equipos Dell Optiplex 380 DT para la Escuela de Turismo de Lanzarote</title>
		<link>http://www.cibernatural.com/equipos-dell-optiplex-380-dt-para-la-escuela-de-turismo-de-lanzarote/</link>
		<comments>http://www.cibernatural.com/equipos-dell-optiplex-380-dt-para-la-escuela-de-turismo-de-lanzarote/#comments</comments>
		<pubDate>Tue, 20 Jul 2010 12:53:42 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Noticias]]></category>
		<category><![CDATA[Venta]]></category>
		<category><![CDATA[DELL]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Lanzarote]]></category>
		<category><![CDATA[Tahiche]]></category>

		<guid isPermaLink="false">http://www.cibernatural.com/?p=664</guid>
		<description><![CDATA[Cibernatural S.L. ha proporcionado recientemente 13 unidades de Equipos DELL Optiplex 380 DT con sus correspondientes monitores DELL E170S a la Escuela Universitaria de Turismo de Lanzarote (EUTL), a través del Excmo. Cabildo de Lanzarote, al mejor precio. La Escuela de Turismo de Lanzarote se une a la lista de empresas y organismos que confían [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.cibernatural.com">Cibernatural S.L.</a> ha proporcionado recientemente 13 unidades de <a href="http://www1.euro.dell.com/content/products/featuresdetails.aspx/optiplex-380?c=es&#038;l=es&#038;s=bsd&#038;cs=esbsdt1&#038;~lt=popup">Equipos DELL Optiplex 380 DT</a> con sus correspondientes monitores DELL E170S a la <a href="http://www.cabildodelanzarote.com/eutl/">Escuela Universitaria de Turismo de Lanzarote (EUTL)</a>, a través del <a href="http://www.cabildodelanzarote.com/">Excmo. Cabildo de Lanzarote</a>, al mejor precio.</p>
<p>La Escuela de Turismo de Lanzarote se une a la lista de empresas y organismos que confían en DELL y en Cibernatural, su socio en Lanzarote &#8211; Islas Canarias.</p>
<p>Puede ver más detalles de los ordenadores de sobremesa DELL Optiplex 380 mediante la <a href="http://www1.euro.dell.com/content/products/superview.aspx?c=es&#038;cs=esbsdr4&#038;l=es&#038;s=bsd&#038;xdb=Z2xvYmFsOnByb2R1Y3RzOm9wdGl4OmZsYXNoOm9wdGlwbGV4LTM4MC1zdXBlcnZpZXcjcmVnaW9u">Galería de Imágenes del DELL Optiplex 380 DT</a></p>

                            <div id="aspdf">
                                <a href="http://www.cibernatural.com/wp-content/plugins/as-pdf/generate.php?post=664">
                                    <span>Descargar en PDF</span>
                                </a>
                            </div>
                        ]]></content:encoded>
			<wfw:commentRss>http://www.cibernatural.com/equipos-dell-optiplex-380-dt-para-la-escuela-de-turismo-de-lanzarote/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Gestión web de clientes y facturación en Django</title>
		<link>http://www.cibernatural.com/gestion-web-de-clientes-y-facturacion-en-django/</link>
		<comments>http://www.cibernatural.com/gestion-web-de-clientes-y-facturacion-en-django/#comments</comments>
		<pubDate>Fri, 16 Jul 2010 16:33:00 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Proyectos]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Facturación]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[ReportLab]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://www.cibernatural.com/?p=656</guid>
		<description><![CDATA[Cibernatural S.L. estrena una nueva y sencilla aplicación web de Gestión de clientes y facturas, presupuestos, partes de entrega y abonos, desarrollada completamente a partir del Sitio de Administración de Django, que proporciona una interfaz limpia y eficiente. Con ella, se puede gestionar una empresa fácilmente en cualquier momento, lugar y desde cualquier dispositivo que [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.cibernatural.com">Cibernatural S.L.</a> estrena una nueva y sencilla <strong>aplicación web de Gestión de clientes y facturas</strong>, presupuestos, partes de entrega y abonos, desarrollada completamente a partir del Sitio de Administración de <strong>Django</strong>, que proporciona una interfaz limpia y eficiente.</p>
<p>Con ella, se puede gestionar una empresa fácilmente en cualquier momento, lugar y desde cualquier dispositivo que disponga de un navegador e Internet (ordenador, portátil, teléfono móvil, etc.), sin necesidad de ninguna instalación,  etc.</p>
<p>La aplicación permite la búsqueda y edición de cualquier cliente, factura, presupuesto, etc., de forma rápida y sencilla, así como la generación en PDF de cualquiera de estos elementos personalizados para la empresa usuaria de la aplicación. Además, permite realizar varias tareas con un solo click, como volcar un presupuesto en una factura, etc., y proporciona multitud de listados estadísticos en PDF como por ejemplo, resúmenes de facturación por meses, por trimestres, por años, por clientes, etc.</p>
<p>Algunas empresas como <a href="http://www.lasillitadecanarias.com">La Sillita de Canarias</a> ya están utilizando esta aplicación, que pretende simplificar la facturación de la empresa y llevarla a la &#8220;nube&#8221; para gestionar su empresa cuándo y donde quiera.</p>

                            <div id="aspdf">
                                <a href="http://www.cibernatural.com/wp-content/plugins/as-pdf/generate.php?post=656">
                                    <span>Descargar en PDF</span>
                                </a>
                            </div>
                        ]]></content:encoded>
			<wfw:commentRss>http://www.cibernatural.com/gestion-web-de-clientes-y-facturacion-en-django/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tutorial de Django &#8211; VI</title>
		<link>http://www.cibernatural.com/tutorial-de-django-vi/</link>
		<comments>http://www.cibernatural.com/tutorial-de-django-vi/#comments</comments>
		<pubDate>Fri, 16 Jul 2010 12:35:28 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Técnico]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://www.cibernatural.com/?p=640</guid>
		<description><![CDATA[Formularios Los formularios HTML son la columna vertebral de sitios Web interactivos. Este capítulo comprende cómo se puede utilizar Django para acceder a los datos de formulario emitidos por el usuario, validarlos, y hacer algo con ellos. En el camino, cubriremos los objetos HttpRequest y Form. Obtener datos del objeto Request Los objetos HttpRequest, tienen una serie [...]]]></description>
			<content:encoded><![CDATA[<h2>Formularios</h2>
<p>Los formularios HTML son la columna vertebral de sitios Web interactivos. Este capítulo comprende cómo se puede utilizar Django para acceder a los datos de formulario emitidos por el usuario, validarlos, y hacer algo con ellos. En el camino, cubriremos los objetos HttpRequest y Form.</p>
<h3>Obtener datos del objeto Request</h3>
<p>Los objetos HttpRequest, tienen una serie de atributos y de métodos que son interesantes y debería familiarizarse con ellos para saber lo que es posible hacer con ellos.</p>
<p>Puede usar estos atributos para obtener información acerca de la petición actual (por ejemplo el usuario / navegador web que está cargando la página actual en su sitio Django) en el momento en que se ejecuta la la función de la vista.</p>
<h3>Información sobre la URL</h3>
<p>Los objetos HttpRequest contienen varias piezas de información sobre la URL solicitada actualmente:</p>
<p>Usar siempre los atributos / métodos en lugar de codificar las direcciones URL en sus vista. Esto hace que el código sea más flexible y que pueda ser reutilizado en otros lugares. He aquí un simple ejemplo: </p>
<blockquote><p>
# BAD!<br />
def current_url_view_bad(request):</p>
<p style="padding-left:30px">	return HttpResponse(&#8220;Welcome to the page at /current/&#8221;)</p>
<p># GOOD<br />
def current_url_view_good(request):</p>
<p style="padding-left:30px">	return HttpResponse(&#8220;Welcome to the page at %s&#8221; % request.path)</p></blockquote>
<h3>Otra información sobre la petición</h3>
<p>request.META es un diccionario Python que contiene todas las cabeceras HTTP disponibles para la solicitud dada, incluyendo la dirección IP del usuario y el agente de usuario (generalmente el nombre y la versión del navegador Web).  Las siguientes son algunas claves comunes en este diccionario:</p>
<ul>
<li>HTTP_REFERER: La URL de referencia, si la hubiese.</li>
<li>HTTP_USER_AGENT: La cadena de agente de usuario (si existe) del navegador del usuario. Esto se parecerá a algo como lo siguiente: &#8220;Mozilla 5.0 (X11; U; Linux i686) Gecko/20080829 Firefox/2.0.0.17&#8243;</li>
<li>REMOTE_ADDR: La dirección IP del cliente.</li>
</ul>
<p>Usted debe usar una cláusula try / except, o un método get() para manejar el caso de claves indefinidas, como en este ejemplo:</p>
<blockquote><p># BAD!<br />
def ua_display_bad(request):</p>
<p style="padding-left:30px">	ua = request.META['HTTP_USER_AGENT'] # Might raise KeyError!<br />
	return HttpResponse(&#8220;Your browser is %s&#8221; % ua)</p>
<p># GOOD (VERSION 1)<br />
def ua_display_good1(request):</p>
<p style="padding-left:30px">	try:</p>
<p style="padding-left:60px">	ua = request.META['HTTP_USER_AGENT']</p>
<p style="padding-left:30px">	except KeyError:</p>
<p style="padding-left:60px">	ua = &#8216;unknown&#8217;</p>
<p style="padding-left:30px">	return HttpResponse(&#8220;Your browser is %s&#8221; % ua)</p>
<p># GOOD (VERSION 2)<br />
def ua_display_good2(request):</p>
<p style="padding-left:30px">	ua = request.META.get(&#8216;HTTP_USER_AGENT&#8217;, &#8216;unknown&#8217;)<br />
	return HttpResponse(&#8220;Your browser is %s&#8221; % ua)</p>
</blockquote>
<p>Le animamos a escribir una pequeña vista que muestra todos los datos de request.META para que pueda conocer lo que está disponible. Se podría parecer a esto:</p>
<blockquote><p>def display_meta(request):</p>
<p style="padding-left:30px">	values = request.META.items()<br />
	values.sort()<br />
	html = []<br />
	for k, v in values:</p>
<p style="padding-left:60px">	html.append(&#8216;&lt;tr&gt;&lt;td&gt;%s&lt;/td&gt;&lt;td&gt;%s&lt;/td&gt;&lt;/tr&gt;&#8217; % (k, v))</p>
<p style="padding-left:30px">	return HttpResponse(&#8216;&lt;table&gt;%s&lt;/table&gt;&#8217; % &#8216;\n&#8217;.join(html))</p>
</blockquote>
<h3>Información sobre datos emitidos</h3>
<p>Más allá de los metadatos básicos acerca de la solicitud, los objetos HttpRequest tienen dos atributos que contienen la información que envía el usuario: request.GET y request.POST. Ambas son objetos como diccionarios que le dan acceso a los datos GET y POST.</p>
<p>Los datos POST generalmente son emitidos desde un formulario HTML, mientras que los datos GET pueden provenir de un formulario o del querystring de la URL de la página.</p>
<h3>Un ejemplo simple de gestión de formulario</h3>
<p>Creemos una vista simple que permita a los usuarios buscar en nuestra base de datos de libros por el título.</p>
<p>En general, hay dos partes para el desarrollo de un formulario: la interfaz de usuario HTML y el código de la vista que procesa los datos presentados. La primera parte es fácil, vamos a crear una vista que muestre un formulario de búsqueda:</p>
<blockquote><p>from django.shortcuts import render_to_response</p>
<p>def search_form(request):</p>
<p style="padding-left:30px">	return render_to_response(&#8216;search_form.html&#8217;)</p>
</blockquote>
<p>Esta vista puede residir en cualquier parte de su ruta de acceso Python. Para este ejemplo, colocarla en <em>books/views.py</em>.</p>
<p>La plantilla de acompañamiento, <em>search_form.html</em>, podría tener este aspecto: </p>
<blockquote><p>
&lt;html&gt;<br />
&lt;head&gt;</p>
<p style="padding-left:30px">&lt;title&gt;Search&lt;/title&gt;</p>
<p>&lt;/head&gt;<br />
&lt;body&gt;</p>
<p style="padding-left:30px">&lt;form action=&#8221;/search/&#8221; method=&#8221;get&#8221;&gt;</p>
<p style="padding-left:60px">&lt;input type=&#8221;text&#8221; name=&#8221;q&#8221;&gt;<br />
		&lt;input type=&#8221;submit&#8221; value=&#8221;Search&#8221;&gt;</p>
<p style="padding-left:30px">&lt;/form&gt;</p>
<p>&lt;/body&gt;<br />
&lt;/html&gt;</p></blockquote>
<p>El patrón URL en <em>urls.py</em> podría ser algo como esto:</p>
<blockquote><p>urlpatterns = patterns(&#8221;,</p>
<p style="padding-left:30px">	# &#8230;<br />
	(r&#8217;^search-form/$&#8217;, views.search_form),<br />
	# &#8230;</p>
<p>)</p></blockquote>
<p>Ahora, si ejecuta el runserver y visita <strong>http://127.0.0.1:8000/search-form/</strong>, verá la interfaz de búsqueda. Bastante simple.</p>
<p>Intente emitir el formulario, sin embargo, y obtendrá un error 404 de Django. El formulario apunta a la URL /search/, que aún no ha sido implementada. Vamos a arreglar eso con una segunda función de vista:</p>
<blockquote><p># urls.py<br />
urlpatterns = patterns(&#8221;,</p>
<p style="padding-left:30px"># &#8230;<br />
	(r&#8217;^search-form/$&#8217;, views.search_form),<br />
	(r&#8217;^search/$&#8217;, views.search),<br />
	# &#8230;</p>
<p>)</p></blockquote>
<blockquote><p># views.py<br />
def search(request):</p>
<p style="padding-left:30px">if &#8216;q&#8217; in request.GET:</p>
<p style="padding-left:60px">message = &#8216;You searched for: %r&#8217; % request.GET['q']</p>
<p style="padding-left:30px">else:</p>
<p style="padding-left:60px">message = &#8216;You submitted an empty form.&#8217;</p>
<p style="padding-left:30px">return HttpResponse(message)</p>
</blockquote>
<p>Por el momento, esto sólo muestra el término de búsqueda del usuario de modo que pueda asegurarse que los datos se presentan a Django correctamente y para que pueda tener una idea de cómo los términos de la búsqueda fluyen a través del sistema. En resumen, esto es lo que sucede:</p>
<ol>
<li>El formulario HTML define una variable q. Cuando se emite, el valor de q se envía a través de GET (method = &#8220;get&#8221;) a la URL /search/.</li>
<li>La vista Django que se encarga de la dirección /search/ tiene acceso al valor de q en request.GET.</li>
</ol>
<p>Tenga en cuenta que comprobamos explícitamente que &#8216;q&#8217; existe en request.GET. Como hemos señalado, usted no debe confiar en nada presentado por los usuarios. Si no ha añadido esta verificación, cualquier emisión de un formulario vacío lanzaría KeyError en la vista:</p>
<blockquote><p># BAD!<br />
def bad_search(request):</p>
<p style="padding-left:30px"># The following line will raise KeyError if &#8216;q&#8217; hasn&#8217;t<br />
	# been submitted!<br />
	message = &#8216;You searched for: %r&#8217; % request.GET['q']<br />
	return HttpResponse(message)</p>
</blockquote>
<p>Los datos POST funcionan de la misma manera que los datos GET. ¿Cuál es la diferencia entre GET y POST?  Utilice GET cuando el acto de emitir el formulario es sólo una solicitud para &#8220;obtener&#8221; datos. Utilice POST siempre que el acto de emitir el formulario tenga algunos efectos secundarios de actualización de datos o enviar un e-mail, o algo más de la simple exhibición de los datos.  En nuestro ejemplo de búsqueda de libro, estamos utilizando GET porque la consulta no cambia ningún dato en nuestro servidor. Ahora que hemos verificado que request.GET se está pasando correctamente, vamos a conectar la consulta de búsqueda del usuario con nuestra base de datos de libros (de nuevo, en <em>views.py</em>):</p>
<blockquote><p>from django.http import HttpResponse<br />
from django.shortcuts import render_to_response<br />
from mysite.books.models import Book</p>
<p>def search(request):</p>
<p style="padding-left:30px">if &#8216;q&#8217; in request.GET and request.GET['q']:</p>
<p style="padding-left:60px">q = request.GET['q']<br />
		books = Book.objects.filter(title__icontains=q)<br />
		return render_to_response(&#8216;search_results.html&#8217;, {&#8216;books&#8217;: books, &#8216;query&#8217;: q})</p>
<p style="padding-left:30px">else:</p>
<p style="padding-left:60px">return HttpResponse(&#8216;Please submit a search term.&#8217;)</p>
</blockquote>
<p>El código de la plantilla  <em>search_results.html</em> podría ser algo como esto:</p>
<blockquote><p>
&lt;p&gt;You searched for: &lt;strong&gt;{{ query }}&lt;/strong&gt;&lt;/p&gt;</p>
<p>{% if books %}</p>
<p style="padding-left:30px">&lt;p&gt;Found {{ books|length }} book{{ books|pluralize }}.&lt;/p&gt;<br />
	&lt;ul&gt;</p>
<p style="padding-left:60px">{% for book in books %}<br />
		&lt;li&gt;{{ book.title }}&lt;/li&gt;<br />
		{% endfor %}</p>
<p style="padding-left:30px">&lt;/ul&gt;</p>
<p>{% else %}</p>
<p style="padding-left:30px">&lt;p&gt;No books matched your search criteria.&lt;/p&gt;</p>
<p>{% endif %}
</p></blockquote>
<h3>Mejorar nuestro ejemplo de gestión de formulario simple</h3>
<p>En primer lugar, nuestra gestión de la vista search() de una consulta vacía es pobre &#8211; estamos mostrando solo un mensaje &#8220;Por favor, envíe un término de búsqueda&#8221;, que requiere que el usuario pulse el botón Atrás del navegador. Esto es horrible y poco profesional.</p>
<p>Sería mucho mejor volver a mostrar el formulario, con un error sobre él, de modo que el usuario pueda volver a intentarlo inmediatamente. La forma más sencilla de hacerlo sería la de renderizar la plantilla de nuevo, de esta forma:</p>
<blockquote><p>from django.http import HttpResponse<br />
from django.shortcuts import render_to_response<br />
from mysite.books.models import Book</p>
<p>def search_form(request):</p>
<p style="padding-left:30px">return render_to_response(&#8216;search_form.html&#8217;)</p>
<p>def search(request):</p>
<p style="padding-left:30px">if &#8216;q&#8217; in request.GET and request.GET['q']:</p>
<p style="padding-left:60px">q = request.GET['q']<br />
		books = Book.objects.filter(title__icontains=q)<br />
		return render_to_response(&#8216;search_results.html&#8217;, {&#8216;books&#8217;: books, &#8216;query&#8217;: q})</p>
<p style="padding-left:30px">else:</p>
<p style="padding-left:60px">return render_to_response(&#8216;search_form.html&#8217;, {&#8216;error&#8217;: True})</p>
</blockquote>
<p>Hemos mejorado search() renderizando la plantilla search_form.html de nuevo, si la consulta está vacía. Y ya que tenemos que mostrar un mensaje de error en esa plantilla, pasamos una variable de plantilla. Ahora podemos editar search_form.html para comprobar la variable de error:</p>
<blockquote><p>
&lt;html&gt;<br />
&lt;head&gt;</p>
<p style="padding-left:30px">&lt;title&gt;Search&lt;/title&gt;</p>
<p>&lt;/head&gt;<br />
&lt;body&gt;</p>
<p style="padding-left:30px">{% if error %}<br />
	&lt;p style=&#8221;color: red;&#8221;&gt;Please submit a search term.&lt;/p&gt;<br />
	{% endif %}<br />
	&lt;form action=&#8221;/search/&#8221; method=&#8221;get&#8221;&gt;</p>
<p style="padding-left:60px">&lt;input type=&#8221;text&#8221; name=&#8221;q&#8221;&gt;<br />
		&lt;input type=&#8221;submit&#8221; value=&#8221;Search&#8221;&gt;</p>
<p style="padding-left:30px">&lt;/form&gt;</p>
<p>&lt;/body&gt;<br />
&lt;/html&gt;
</p></blockquote>
<p>Todavía podemos utilizar esta plantilla desde nuestra vista original, search_form(), ya que search_form() no pasa el error a la plantilla &#8211; el mensaje de error no se mostrará en ese caso.</p>
<p>Con este cambio es una aplicación mejor, pero ahora surge la pregunta: ¿es una vista search_form() dedicada realmente necesaria? Tal como está, una solicitud a la dirección URL /search/ (sin los parámetros GET) mostrará el formulario vacío (pero con un error). Podemos eliminar la vista search_form(), junto con su patrón URL asociado, siempre y cuando cambiemos search() para ocultar el mensaje de error cuando alguien visite /search/ sin parámetros GET:</p>
<blockquote><p>def search(request):</p>
<p style="padding-left:30px">error = False<br />
	if &#8216;q&#8217; in request.GET:</p>
<p style="padding-left:60px">q = request.GET['q']<br />
		if not q:</p>
<p style="padding-left:90px">error = True</p>
<p style="padding-left:60px">else:</p>
<p style="padding-left:90px">books = Book.objects.filter(title__icontains=q)<br />
			return render_to_response(&#8216;search_results.html&#8217;, {&#8216;books&#8217;: books, &#8216;query&#8217;: q})</p>
<p style="padding-left:30px">return render_to_response(&#8216;search_form.html&#8217;, {&#8216;error&#8217;: error})</p>
</blockquote>
<p>En esta vista actualizada, si un usuario visita /search/ sin parámetros GET, verá el formulario de búsqueda sin mensaje de error. Si un usuario envía el formulario con un valor vacío para &#8216;q&#8217;, verá el formulario de búsqueda con un mensaje de error. Y, por último, si un usuario envía el formulario con un valor no vacío de &#8216;q&#8217;, verá los resultados de búsqueda.</p>
<p>Podemos hacer una mejora final a esta aplicación, para quitar un poco de redundancia. Ahora que hemos mezclado las dos vistas y URLs en una sola y /search/ maneja tanto la pantalla del formulario de búsqueda como la del resultado, el formulario HTML en <em>search_form.html</em> no tiene que codificar una URL a pelo. En lugar de esto:</p>
<blockquote><form action="/search/" method="get"></blockquote>
<p>puede cambiarse a:</p>
<blockquote><form action="" method="get"></blockquote>
<p>El action = &#8220;&#8221; significa &#8220;Enviar el formulario a la misma URL que la página actual.&#8221; Con este cambio, usted no tendrá que acordarse de cambiar la acción, incluso si alguna vez la vista search() apunta a otra URL.</p>
<h3>Validación simple</h3>
<p>Nuestro ejemplo de búsqueda es todavía bastante simple, especialmente en términos de validación de sus datos, hacemos una mera comprobación para asegurar que la consulta de búsqueda no está vacía. Muchos de los formularios HTML incluyen un nivel de validación que es más complejo que asegurar que el valor no está vacío. Todos hemos visto la siguientes mensajes de error en los sitios Web:<br />
&#8220;Por favor introduzca una dirección de correo electrónico&#8221;.<br />
&#8220;Por favor, introduzca un código postal de cinco dígitos válido EE.UU.&#8221;<br />
&#8220;Por favor, introduzca una fecha válida con formato AAAA-MM-DD&#8221;.<br />
&#8220;Por favor, introduzca una contraseña que sea por lo menos de 8 caracteres de longitud y contenga al menos un número&#8221;. </p>
<p>Vamos a afinar la vista search() para validar que el término de búsqueda sea menor o igual a 20 caracteres de largo. ¿Cómo podemos hacerlo? Lo más sencillo sería integrar la lógica directamente en la vista, así:</p>
<blockquote><p>def search(request):</p>
<p style="padding-left:30px">error = False<br />
	if &#8216;q&#8217; in request.GET:</p>
<p style="padding-left:60px">q = request.GET['q']<br />
		if not q:</p>
<p style="padding-left:90px">error = True</p>
<p style="padding-left:60px">elif len(q) > 20:</p>
<p style="padding-left:90px">error = True</p>
<p style="padding-left:60px">else:</p>
<p style="padding-left:90px">books = Book.objects.filter(title__icontains=q)<br />
			return render_to_response(&#8216;search_results.html&#8217;,{&#8216;books&#8217;: books, &#8216;query&#8217;: q})</p>
<p style="padding-left:30px">	return render_to_response(&#8216;search_form.html&#8217;, {&#8216;error&#8217;: error})</p>
</blockquote>
<p>Ahora bien, si se intenta emitir una consulta de búsqueda mayor de 20 caracteres de largo, obtendrá un mensaje de error. Pero ese mensaje de error en <em>search_form.html</em> actualmente dice: &#8220;Por favor, introduzca un término de búsqueda.&#8221;, así que tendremos que cambiarlo para ser exactos en ambos casos (una búsqueda vacía o un término de búsqueda demasiado largo).</p>
<blockquote><p>
&lt;html&gt;<br />
&lt;head&gt;</p>
<p style="padding-left:30px">&lt;title&gt;Search&lt;/title&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;</p>
<p style="padding-left:30px">{% if error %}<br />
	&lt;p style=&#8221;color: red;&#8221;&gt;</p>
<p style="padding-left:60px">Please submit a search term<br />
		20 characters or shorter.</p>
<p style="padding-left:30px">&lt;/p&gt;<br />
	{% endif %}<br />
	&lt;form action=&#8221;/search/&#8221; method=&#8221;get&#8221;&gt;</p>
<p style="padding-left:60px">&lt;input type=&#8221;text&#8221; name=&#8221;q&#8221;&gt;<br />
		&lt;input type=&#8221;submit&#8221; value=&#8221;Search&#8221;&gt;</p>
<p style="padding-left:30px">&lt;/form&gt;</p>
<p>&lt;/body&gt;<br />
&lt;/html&gt;</p></blockquote>
<p>Hay algo mal en esto. Nuestro único mensaje de error es potencialmente confuso. ¿Por qué el mensaje de error para un valor vacío menciona nada sobre un límite de 20 caracteres? Los mensajes de error deben ser específicos y claros.</p>
<p>El problema es que estamos utilizando un solo valor boolean para el error, cuando habría que utilizar una lista de cadenas de mensajes de error. He aquí cómo podemos solucionarlo:</p>
<blockquote><p>def search(request):</p>
<p style="padding-left:30px">errors = []<br />
	if &#8216;q&#8217; in request.GET:</p>
<p style="padding-left:60px">q = request.GET['q']<br />
		if not q:</p>
<p style="padding-left:90px">errors.append(&#8216;Enter a search term.&#8217;)</p>
<p style="padding-left:60px">elif len(q) > 20:</p>
<p style="padding-left:90px">errors.append(&#8216;Please enter at most 20 characters.&#8217;)</p>
<p style="padding-left:60px">else:</p>
<p style="padding-left:90px">books = Book.objects.filter(title__icontains=q)<br />
			return render_to_response(&#8216;search_results.html&#8217;, {&#8216;books&#8217;: books, &#8216;query&#8217;: q})</p>
<p style="padding-left:30px">return render_to_response(&#8216;search_form.html&#8217;, {&#8216;errors&#8217;: errors})</p>
</blockquote>
<p>Entonces tenemos que hacer un pequeño ajuste en la plantilla <em>search_form.html</em> para reflejar que se pasó una lista de errores, en lugar de un error boolean: </p>
<blockquote><p>
&lt;html&gt;<br />
&lt;head&gt;</p>
<p style="padding-left:30px">&lt;title&gt;Search&lt;/title&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;</p>
<p style="padding-left:30px">{% if errors %}<br />
	&lt;ul&gt;</p>
<p style="padding-left:60px">{% for error in errors %}</p>
<p style="padding-left:90px">&lt;li&gt;{{ error }}&lt;/li&gt;</p>
<p style="padding-left:60px">{% endfor %}</p>
<p style="padding-left:30px">&lt;/ul&gt;<br />
	{% endif %}<br />
	&lt;form action=&#8221;/search/&#8221; method=&#8221;get&#8221;&gt;</p>
<p style="padding-left:60px">&lt;input type=&#8221;text&#8221; name=&#8221;q&#8221;&gt;<br />
		&lt;input type=&#8221;submit&#8221; value=&#8221;Search&#8221;&gt;</p>
<p style="padding-left:30px">&lt;/form&gt;</p>
<p>&lt;/body&gt;<br />
&lt;/html&gt;</p></blockquote>
<h3>Hacer un formulario de contacto</h3>
<p>Aunque hemos reiterado en el ejemplo del formulario de búsqueda de libros varias veces y lo hemos mejorado, sigue siendo muy sencillo: basta con un solo campo, &#8216;q&#8217;. Al ser tan simple, ni siquiera usamos la librería de formularios de Django para tratar con él. Pero las formas más complejas requieren tratamientos más complejos, y ahora vamos a desarrollar algo más complejo: un formulario de contacto de la web que permite a los usuarios del sitio envíar comentarios, junto con un e-mail de retorno. Después que el formulario es emitido y los datos son validados, automáticamente le enviaremos un mensaje por correo electrónico al personal del sitio.</p>
<p>Empezaremos con nuestra plantilla, <em>contact_form.html</em>:</p>
<blockquote><p>
&lt;html&gt;<br />
&lt;head&gt;</p>
<p style="padding-left:30px">&lt;title&gt;Contact us&lt;/title&gt;</p>
<p>&lt;/head&gt;<br />
&lt;body&gt;</p>
<p style="padding-left:30px">&lt;h1&gt;Contact us&lt;/h1&gt;<br />
	{% if errors %}<br />
	&lt;ul&gt;</p>
<p style="padding-left:60px">{% for error in errors %}<br />
		&lt;li&gt;{{ error }}&lt;/li&gt;<br />
		{% endfor %}</p>
<p style="padding-left:30px">&lt;/ul&gt;<br />
	{% endif %}</p>
<p>	&lt;form action=&#8221;/contact/&#8221; method=&#8221;post&#8221;&gt;</p>
<p style="padding-left:60px">&lt;p&gt;Subject: &lt;input type=&#8221;text&#8221; name=&#8221;subject&#8221;&gt;&lt;/p&gt;<br />
		&lt;p&gt;Your e-mail (optional): &lt;input type=&#8221;text&#8221; name=&#8221;e-mail&#8221;&gt;&lt;/p&gt;<br />
		&lt;p&gt;Message: &lt;textarea name=&#8221;message&#8221; rows=&#8221;10&#8243; cols=&#8221;50&#8243;&gt;&lt;/textarea&gt;&lt;/p&gt;<br />
		&lt;input type=&#8221;submit&#8221; value=&#8221;Submit&#8221;&gt;</p>
<p style="padding-left:30px">&lt;/form&gt;</p>
<p>&lt;/body&gt;<br />
&lt;/html&gt;
</p></blockquote>
<p>Hemos definido tres campos: el asunto, la dirección de correo electrónico y el mensaje. El segundo es opcional, pero los otros dos campos son obligatorios. Tenga en cuenta que estamos usando method = &#8220;post&#8221; aquí en lugar de method = &#8220;get&#8221; ya que este formulario de emisión tiene un efecto secundario &#8211; que envía un e-mail. Si seguimos el camino establecido por nuestra vista search() de la sección anterior, una versión de nuestra vista contact() podría tener este aspecto:</p>
<blockquote><p>from django.core.mail import send_mail<br />
from django.http import HttpResponseRedirect<br />
from django.shortcuts import render_to_response</p>
<p>def contact(request):</p>
<p style="padding-left:30px">errors = []<br />
	if request.method == &#8216;POST&#8217;:</p>
<p style="padding-left:60px">if not request.POST.get(&#8216;subject&#8217;, &#8221;):</p>
<p style="padding-left:90px">errors.append(&#8216;Enter a subject.&#8217;)</p>
<p style="padding-left:60px">if not request.POST.get(&#8216;message&#8217;, &#8221;):</p>
<p style="padding-left:90px">errors.append(&#8216;Enter a message.&#8217;)</p>
<p style="padding-left:60px">if request.POST.get(&#8216;e-mail&#8217;) and &#8216;@&#8217; not in request.POST['e-mail']:</p>
<p style="padding-left:90px">errors.append(&#8216;Enter a valid e-mail address.&#8217;)</p>
<p style="padding-left:60px">if not errors:</p>
<p style="padding-left:90px">send_mail(request.POST['subject'], request.POST['message'],<br />
					request.POST.get(&#8216;e-mail&#8217;, &#8216;noreply@example.com&#8217;),<br />
					['siteowner@example.com'],</p>
<p style="padding-left:60px">)<br />
			return HttpResponseRedirect(&#8216;/contact/thanks/&#8217;)</p>
<p style="padding-left:30px">return render_to_response(&#8216;contact_form.html&#8217;,{&#8216;errors&#8217;: errors})</p>
</blockquote>
<p>Varias cosas nuevas están sucediendo aquí:</p>
<ul>
<li>Comprobamos que request.method es &#8216;POST&#8217;. Esto será cierto sólo en el caso de una emisión del formulario, no será cierto si alguien está solamente viendo el formulario de contacto. Esto hace que sea una buena forma de aislar el caso de &#8220;pantalla del formulario&#8221; del caso de&#8221;transformación del formulario&#8221;.</li>
<li>En lugar de request.GET, estamos usando request.POST para acceder a los datos del formulario emitido. Esto es necesario porque el código de contact_form.html usa method = &#8220;post&#8221;.</li>
<li>Contamos con dos campos obligatorios, asunto y mensaje, así que tenemos que validar ambos. Notar que estamos utilizando request.POST.get() y proporcionando una cadena en blanco como el valor por defecto.</li>
<li>Aunque el campo de correo electrónico no es obligatorio, debemos aún validarlo si es emitido. Nuestro algoritmo de validación aquí es frágil &#8211; estamos comprobando que la cadena contiene un carácter @. En el mundo real, necesitaríamos una validación más robusta.</li>
<li>Estamos usando la función django.core.mail.send_mail para enviar un e-mail. Esta función tiene cuatro argumentos necesarios: el asunto del e-mail, el cuerpo del correo electrónico, la dirección del emisor, y una lista de direcciones de los destinatarios. send_mail está contenida en la clase de Django E-mailMessage, que proporciona características avanzadas tales como archivos adjuntos, emails multiplart, y el control total de los encabezados del correo electrónico. </li>
<li>Después de enviar el e-mail, redirigimos a una página de &#8220;éxito&#8221; devolviendo un objeto HttpResponseRedirect. Usted siempre debe enviar una redirección para el éxito de las peticiones POST. Es una de las mejores prácticas del desarrollo web.</li>
</ul>
<p>Esto vista funciona, pero las funciones de validación son enrevesadas. Imagine la tramitación de un formulario con una docena de campos, ¿de verdad quieres tener que escribir todas esas sentencias if?</p>
<p>Otro problema es volver a mostrar el formulario. En el caso de errores de validación, es mejor práctica volver a mostrar el formulario con los datos presentados anteriormente ya rellenados para que el usuario puede ver lo que hizo mal (y no tener que volver a introducir los datos en los campos que ha emitido correctamente). Nosotros manualmente podríamos pasar los datos POST de nuevo a la plantilla, pero habría que editar cada campo HTML para insertar el valor adecuado en el lugar adecuado: </p>
<blockquote><p># views.py<br />
def contact(request):</p>
<p style="padding-left:30px">errors = []<br />
	if request.method == &#8216;POST&#8217;:</p>
<p style="padding-left:60px">if not request.POST.get(&#8216;subject&#8217;, &#8221;):</p>
<p style="padding-left:90px">errors.append(&#8216;Enter a subject.&#8217;)</p>
<p style="padding-left:60px">if not request.POST.get(&#8216;message&#8217;, &#8221;):</p>
<p style="padding-left:90px">errors.append(&#8216;Enter a message.&#8217;)</p>
<p style="padding-left:60px">if request.POST.get(&#8216;e-mail&#8217;) and &#8216;@&#8217; not in request.POST['e-mail']:</p>
<p style="padding-left:90px">errors.append(&#8216;Enter a valid e-mail address.&#8217;)</p>
<p style="padding-left:60px">if not errors:</p>
<p style="padding-left:90px">send_mail(<br />
				request.POST['subject'],<br />
				request.POST['message'],<br />
				request.POST.get(&#8216;e-mail&#8217;, &#8216;noreply@example.com&#8217;),<br />
				['siteowner@example.com'],<br />
			)</p>
<p style="padding-left:60px">return HttpResponseRedirect(&#8216;/contact/thanks/&#8217;)</p>
<p style="padding-left:30px">return render_to_response(&#8216;contact_form.html&#8217;, {&#8216;errors&#8217;: errors,&#8217;subject&#8217;: request.POST.get(&#8216;subject&#8217;, &#8221;),</p>
<p style="padding-left:90px">&#8216;message&#8217;: request.POST.get(&#8216;message&#8217;, &#8221;),&#8217;e-mail&#8217;: request.POST.get(&#8216;e-mail&#8217;, &#8221;),</p>
<p style="padding-left:30px">})</p>
</blockquote>
<blockquote><p>
# contact_form.html<br />
&lt;html&gt;<br />
&lt;head&gt;</p>
<p style="padding-left:30px">&lt;title&gt;Contact us&lt;/title&gt;</p>
<p>&lt;/head&gt;<br />
&lt;body&gt;</p>
<p style="padding-left:30px">&lt;h1&gt;Contact us&lt;/h1&gt;<br />
	{% if errors %}<br />
	&lt;ul&gt;</p>
<p style="padding-left:60px">{% for error in errors %}<br />
		&lt;li&gt;{{ error }}&lt;/li&gt;<br />
		{% endfor %}</p>
<p style="padding-left:30px">&lt;/ul&gt;<br />
	{% endif %}<br />
	&lt;form action=&#8221;/contact/&#8221; method=&#8221;post&#8221;&gt;</p>
<p style="padding-left:60px">&lt;p&gt;Subject: &lt;input type=&#8221;text&#8221; name=&#8221;subject&#8221; value=&#8221;{{ subject }}&#8221;>&lt;/p&gt;<br />
		&lt;p&gt;Your e-mail (optional):</p>
<p style="padding-left:90px">&lt;input type=&#8221;text&#8221; name=&#8221;e-mail&#8221; value=&#8221;{{ e-mail }}&#8221;&gt;</p>
<p style="padding-left:60px">&lt;/p&gt;<br />
		&lt;p&gt;Message:
<p style="padding-left:90px">&lt;textarea name=&#8221;message&#8221; rows=&#8221;10&#8243; cols=&#8221;50&#8243;&gt;<br />
			**{{ message }}**<br />
			&lt;/textarea&gt;</p>
<p style="padding-left:60px">&lt;/p&gt;<br />
		&lt;input type=&#8221;submit&#8221; value=&#8221;Submit&#8221;&gt;</p>
<p style="padding-left:30px">&lt;/form&gt;</p>
<p>&lt;/body&gt;<br />
&lt;/html&gt;
</p></blockquote>
<p>Se trata de una gran cantidad de código, e introduce un montón de posibilidades de error humano. Veremos alguna librería de alto nivel que gestione las tareas relacionadas con los formularios y la validación.</p>
<h3>Su primera clase de formulario</h3>
<p>Django viene con una librería de formularios, llamada django.forms, que se encarga de muchas de las cuestiones que hemos estado viendo en este capítulo – desde la muestra de formularios HTML a la validación. Volvamos a hacer nuestro formulario de contacto utilizando el marco de formularios de Django.</p>
<p>La principal manera de utilizar el marco de formularios es definir una clase Form por cada<br />
<form> HTML. En nuestro caso, sólo tenemos una<br />
<form>, así que tendremos una clase Form. Esta clase puede residir en cualquier lugar, incluyendo directamente en el archivo views.py, pero la convención es mantener las clases Form en un archivo llamado <em>forms.py</em>. Cree este archivo en el mismo directorio que el <em>views.py</em>, y escriba lo siguiente:</p>
<blockquote><p>from django import forms</p>
<p>class ContactForm(forms.Form):</p>
<p style="padding-left:30px">subject = forms.CharField()<br />
	e-mail = forms.EmailField(required=False)<br />
	message = forms.CharField()</p>
</blockquote>
<p>Esto es bastante intuitivo, y es similar a la sintaxis de modelo de Django. Cada campo en el formulario está representado por un tipo de clase Field &#8211; Charfield y EmailField son los únicos tipos Field utilizados aquí &#8211; como atributos de una clase Form. Cada campo es obligatorio por defecto, así que para hacer e-mail opcional, especificamos required = False.</p>
<p>Vayamos al intérprete interactivo de Python y veamos lo que esta clase puede hacer. Lo primero que puede hacer es mostrarse como HTML:</p>
<blockquote><p>
&lt;&lt;&lt; from contact.forms import ContactForm<br />
&lt;&lt;&lt; f = ContactForm()<br />
&lt;&lt;&lt; print f<br />
&lt;tr&gt;&lt;th&gt;&lt;label for=&#8221;id_subject&#8221;&gt;Subject:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;<br />
&lt;input type=&#8221;text&#8221; name=&#8221;subject&#8221; id=&#8221;id_subject&#8221; /&gt;&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;th&gt;&lt;label for=&#8221;id_e-mail&#8221;&gt;E-mail:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;<br />
&lt;input type=&#8221;text&#8221; name=&#8221;e-mail&#8221; id=&#8221;id_e-mail&#8221; /&gt;&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;th&gt;&lt;label for=&#8221;id_message&#8221;&gt;Message:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;<br />
&lt;input type=&#8221;text&#8221; name=&#8221;message&#8221; id=&#8221;id_message&#8221; /&gt;&lt;/td&gt;&lt;/tr&gt;
</p></blockquote>
<p>Django añade una etiqueta a cada campo, junto con las etiquetas <label> para accesibilidad. La idea es hacer que el comportamiento por defecto sea tan satisfactorio como sea posible.</p>
<p>Esta es la salida por defecto en el formato de un &lt;table&gt; HTML, pero hay otras salidas preconstruidas:</p>
<blockquote><p>
&lt;&lt;&lt; print f.as_ul()<br />
&lt;li&gt;&lt;label for=&#8221;id_subject&#8221;&gt;Subject:&lt;/label&gt;<br />
&lt;input type=&#8221;text&#8221; name=&#8221;subject&#8221; id=&#8221;id_subject&#8221; /&gt;&lt;/li&gt;<br />
&lt;li&gt;&lt;label for=&#8221;id_e-mail&#8221;&gt;E-mail:&lt;/label&gt;<br />
&lt;input type=&#8221;text&#8221; name=&#8221;e-mail&#8221; id=&#8221;id_e-mail&#8221; /&gt;&lt;/li&gt;<br />
&lt;li&gt;&lt;label for=&#8221;id_message&#8221;&gt;Message:&lt;/label&gt;<br />
&lt;input type=&#8221;text&#8221; name=&#8221;message&#8221; id=&#8221;id_message&#8221; /&gt;&lt;/li&gt;
</p></blockquote>
<blockquote><p>
&lt;&lt;&lt; print f.as_p()<br />
&#038;ltp>&#038;ltlabel for=&#8221;id_subject&#8221;&gt;Subject:&#038;lt/label&gt;<br />
&#038;ltinput type=&#8221;text&#8221; name=&#8221;subject&#8221; id=&#8221;id_subject&#8221; /&gt;&#038;lt/p&gt;<br />
&#038;ltp>&#038;ltlabel for=&#8221;id_e-mail&#8221;&gt;E-mail:&#038;lt/label&gt;<br />
&#038;ltinput type=&#8221;text&#8221; name=&#8221;e-mail&#8221; id=&#8221;id_e-mail&#8221; /&gt;&#038;lt/p&gt;<br />
&#038;ltp>&#038;ltlabel for=&#8221;id_message&#8221;&gt;Message:&#038;lt/label&gt;<br />
&#038;ltinput type=&#8221;text&#8221; name=&#8221;message&#8221; id=&#8221;id_message&#8221; /&gt;&#038;lt/p&gt;
</p></blockquote>
<p>Tenga en cuenta que la etiquetas de apertura y cierre &lt;table&gt;, &lt;ul&gt;, y &lt;form&gt; no son incluidas en la salida, de forma que usted puede agregar las filas adicionales si es necesario.</p>
<p>Estos métodos son métodos abreviados para el caso común de &#8220;mostrar todo el formulario.&#8221; Usted también puede mostrar el código HTML de un campo particular:</p>
<blockquote><p>
&lt;&lt;&lt; print f['subject']<br />
&lt;input type=&#8221;text&#8221; name=&#8221;subject&#8221; id=&#8221;id_subject&#8221; /&gt;<br />
&lt;&lt;&lt; print f['message']<br />
&lt;input type=&#8221;text&#8221; name=&#8221;message&#8221; id=&#8221;id_message&#8221; /&gt;
</p></blockquote>
<p>La segunda cosa que podemos hacer con los objetos Form es validar los datos. Para ello, crear un nuevo objeto Form y pasarle un diccionario de datos que asigne los nombres de campo a los datos:</p>
<blockquote><p>
&lt;&lt;&lt; f = ContactForm({&#8216;subject&#8217;: &#8216;Hello&#8217;, &#8216;e-mail&#8217;: &#8216;adrian@example.com&#8217;,<br />
… &#8216;message&#8217;: &#8216;Nice site!&#8217;})</p></blockquote>
<p>Una vez ha asociado los datos con una instancia Form, ha creado un Form bound (ligado):</p>
<blockquote><p>
&lt;&lt;&lt; f.is_bound<br />
True</p></blockquote>
<p>Llame al método is_valid() de cualquier Form ligado para averiguar si sus datos son válidos. Hemos pasado un valor válido para cada campo, por lo que el formulario en su totalidad es válido:</p>
<blockquote><p>
&lt;&lt;&lt; f.is_valid()<br />
True</p></blockquote>
<p>Si no se pasa el campo de correo electrónico, sigue siendo válido, porque hemos especificado required = False para ese campo: </p>
<blockquote><p>
&lt;&lt;&lt; f = ContactForm({&#8216;subject&#8217;: &#8216;Hello&#8217;, &#8216;message&#8217;: &#8216;Nice site!&#8217;})<br />
&lt;&lt;&lt; f.is_valid()<br />
True
</p></blockquote>
<p>Pero si dejamos fuera el asunto o el mensaje, el formulario ya no es válido:</p>
<blockquote><p>
&lt;&lt;&lt; f = ContactForm({&#8216;subject&#8217;: &#8216;Hello&#8217;})<br />
&lt;&lt;&lt; f.is_valid()<br />
False<br />
&lt;&lt;&lt; f = ContactForm({&#8216;subject&#8217;: &#8216;Hello&#8217;, &#8216;message&#8217;: &#8221;})<br />
&lt;&lt;&lt; f.is_valid()<br />
False
</p></blockquote>
<p>Usted puede ver los detalles obteniendo mensajes de error específicos por campo:</p>
<blockquote><p>
&lt;&lt;&lt; f = ContactForm({&#8216;subject&#8217;: &#8216;Hello&#8217;, &#8216;message&#8217;: &#8221;})<br />
&lt;&lt;&lt; f['message'].errors<br />
[u'This field is required.']<br />
&lt;&lt;&lt; f['subject'].errors<br />
[]<br />
&lt;&lt;&lt; f['e-mail'].errors<br />
[]
</p></blockquote>
<p>Cada instancia Form ligada tiene un atributo errors que proporciona un diccionario que asigna nombres de campos a listas de mensajes de error:</p>
<blockquote><p>
&lt;&lt;&lt; f = ContactForm({&#8216;subject&#8217;: &#8216;Hello&#8217;, &#8216;message&#8217;: &#8221;})<br />
&lt;&lt;&lt; f.errors<br />
{&#8216;message&#8217;: [u'This field is required.']}</p></blockquote>
<p>Por último, para los casos de instancias de formulario cuyos datos se ha encontrado ser válidos, está disponible el atributo cleaned_data. Este es un diccionario de los datos emitidos, &#8220;limpiado&#8221;. El marco de formularios de Django, no sólo valida los datos, sino que los limpia para convertir de valores a los tipos de Python adecuados, como se muestra aquí:</p>
<blockquote><p>
&lt;&lt;&lt; f = ContactForm({&#8216;subject&#8217;: &#8216;Hello&#8217;, &#8216;e-mail&#8217;: &#8216;adrian@example.com&#8217;,<br />
… &#8216;message&#8217;: &#8216;Nice site!&#8217;})<br />
&lt;&lt;&lt; f.is_valid()<br />
True<br />
&lt;&lt;&lt; f.cleaned_data<br />
{&#8216;message&#8217;: u&#8217;Nice site!&#8217;, &#8216;e-mail&#8217;: u&#8217;adrian@example.com&#8217;, &#8216;subject&#8217;: u&#8217;Hello&#8217;}
</p></blockquote>
<p>Nuestro formulario de contacto trata sólo con cadenas, que son &#8220;limpiadas&#8221; a objetos Unicode, pero si tuviéramos que utilizar un IntegerField o un DateField, el marco de formularios garantizaría que cleaned_data utiliza enteros Python adecuados u objetos datetime.date para los campos dados.</p>
<h3>Vincular objetos Form en vistas</h3>
<p>Ahora que tiene algunos conocimientos básicos sobre las clases Form, veremos cómo podemos utilizar esta infraestructura para sustituir algo en nuestra vista contact(). He aquí cómo podemos reescribir contacto() para utilizar el marco de formularios:</p>
<blockquote><p># views.py</p>
<p>from django.shortcuts import render_to_response<br />
from mysite.contact.forms import ContactForm</p>
<p>def contact(request):</p>
<p style="padding-left:30px">if request.method == &#8216;POST&#8217;:</p>
<p style="padding-left:60px">form = ContactForm(request.POST)<br />
		if form.is_valid():</p>
<p style="padding-left:90px">cd = form.cleaned_data<br />
			send_mail(</p>
<p style="padding-left:120px">cd['subject'],<br />
				cd['message'],<br />
				cd.get(&#8216;e-mail&#8217;, &#8216;noreply@example.com&#8217;),<br />
				['siteowner@example.com'],</p>
<p style="padding-left:90px">)<br />
			return HttpResponseRedirect(&#8216;/contact/thanks/&#8217;)</p>
<p style="padding-left:30px">else:</p>
<p style="padding-left:60px">form = ContactForm()<br />
		return render_to_response(&#8216;contact_form.html&#8217;, {&#8216;form&#8217;: form})</p>
</blockquote>
<blockquote><p>
# contact_form.html</p>
<p>&lt;html&gt;<br />
&lt;head&gt;</p>
<p style="padding-left:30px">&lt;title&gt;Contact us&lt;/title&gt;</p>
<p>&lt;/head&gt;<br />
&lt;body&gt;</p>
<p style="padding-left:30px">&lt;h1&gt;Contact us&lt;/h1&gt;<br />
	{% if form.errors %}</p>
<p style="padding-left:60px">&lt;p style=&#8221;color: red;&#8221;&gt;</p>
<p style="padding-left:90px">Please correct the error{{ form.errors|pluralize }} below.</p>
<p style="padding-left:60px">&lt;/p&gt;</p>
<p style="padding-left:30px">{% endif %}<br />
	&lt;form action=&#8221;" method=&#8221;post&#8221;&gt;</p>
<p style="padding-left:60px">&lt;table&gt;</p>
<p style="padding-left:90px">{{ form.as_table }}</p>
<p style="padding-left:60px">&lt;/table&gt;<br />
		&lt;input type=&#8221;submit&#8221; value=&#8221;Submit&#8221;&gt;</p>
<p style="padding-left:30px">&lt;/form&gt;</p>
<p>&lt;/body&gt;<br />
&lt;/html&gt;
</p></blockquote>
<p>El marco de formularios de Django gestiona la pantalla del HTML, la validación, la limpieza de los datos, y el volver a mostrar el formulario con errores.</p>
<p>Trate de ejecutar esto en local. Cargue el formulario, emítalo sin rellenar ningun campo, emítalo con una dirección inválida de correo electrónico, y finalmente emítalo con los datos válidos.</p>
<h3>Cambiar como se renderizan los campos</h3>
<p>Probablemente lo primero que notará cuando renderiza el formulario localmente es que el campo mensaje se muestra como un &lt;input type=&#8221;text&#8221;&gt;, y debería ser un &lt;textarea&gt;. Podemos arreglar esto mediante el establecimiento del widget del campo:</p>
<blockquote><p>from django import forms</p>
<p>class ContactForm(forms.Form):</p>
<p style="padding-left:30px">subject = forms.CharField()<br />
	e-mail = forms.EmailField(required=False)<br />
	message = forms.CharField(widget=forms.Textarea</p>
</blockquote>
<p>El marco de formularios separa la lógica de presentación de cada campo en un conjunto de widgets. Cada tipo de campo tiene un widget por defecto, pero se puede reemplazar la configuración predeterminada o proporcionar un widget personalizado.</p>
<p>Piense en las clases Field como la representación de la lógica de validación, mientras que los widgets representan la lógica de presentación.</p>
<h3>Establecer una longitud máxima</h3>
<p>Una de las necesidades de validación más común es comprobar que un campo es de un tamaño determinado.Mejoremos nuestro formulario de contacto limitando el asunto a 100 caracteres. Para hacer eso, proporcionar un max_length al Charfield, de esta forma:</p>
<blockquote><p>from django import forms</p>
<p>class ContactForm(forms.Form):</p>
<p style="padding-left:30px">subject = forms.CharField(max_length=100)<br />
	e-mail = forms.EmailField(required=False)<br />
	message = forms.CharField(widget=forms.Textarea)</p>
</blockquote>
<p>Un argumento opcional min_length también está disponible.</p>
<h3>Establecer valores iniciales</h3>
<p>Como una mejora a este formulario, vamos a añadir un valor inicial para el campo Asunto: &#8220;I love your site! &#8220;.  Para hacer esto, podemos utilizar el argumento initial cuando creamos una instancia Form:</p>
<blockquote><p>
def contact(request):</p>
<p style="padding-left:30px">if request.method == &#8216;POST&#8217;:</p>
<p style="padding-left:60px">form = ContactForm(request.POST)<br />
		if form.is_valid():</p>
<p style="padding-left:90px">cd = form.cleaned_data<br />
			send_mail(</p>
<p style="padding-left:120px">cd['subject'],<br />
				cd['message'],<br />
				cd.get(&#8216;e-mail&#8217;, &#8216;noreply@example.com&#8217;),<br />
				['siteowner@example.com'],</p>
<p style="padding-left:90px">)<br />
			return HttpResponseRedirect(&#8216;/contact/thanks/&#8217;)</p>
<p style="padding-left:30px">else:</p>
<p style="padding-left:60px">form = ContactForm(initial={&#8216;subject&#8217;: &#8216;I love your site!&#8217;})<br />
		return render_to_response(&#8216;contact_form.html&#8217;, {&#8216;form&#8217;: form})</p>
</blockquote>
<p>Tenga en cuenta que hay una diferencia entre pasar datos inicialmente y pasar datos que se ligan al formulario. Si sólo pasa los datos iniciales, el formulario será independiente, lo que significa que no tendrá ningún mensaje de error.</p>
<h3>Añadir reglas de validación personalizadas</h3>
<p>Decidimos adoptar una nueva política de validación: cuatro palabras o más, por favor.</p>
<p>Hay varias maneras de conectar la validación personalizada a un formulario Django. Si nuestra regla es algo que volverá a utilizar una y otra vez, podemos crear un tipo de campo personalizado. La mayoría de las validaciones personalizadas son asuntos de una sola vez, sin embargo, y puede vincularse directamente a la clase Form.</p>
<p>Queremos validación adicional en el campo del mensaje, por lo que añadimos un método clean_message() a nuestra clase Form: </p>
<blockquote><p>from django import forms</p>
<p>class ContactForm(forms.Form):</p>
<p style="padding-left:30px">subject = forms.CharField(max_length=100)<br />
	e-mail = forms.EmailField(required=False)<br />
	message = forms.CharField(widget=forms.Textarea)</p>
<p>	def clean_message(self):</p>
<p style="padding-left:60px">message = self.cleaned_data['message']<br />
		num_words = len(message.split())<br />
		if num_words < 4:</p>
<p style="padding-left:90px">raise forms.ValidationError(&#8220;Not enough words!&#8221;)</p>
<p style="padding-left:60px">return message</p>
</blockquote>
<p>El sistema de formularios de Django busca automáticamente cualquier método cuyo nombre empieza con clean_ y termina con el nombre de un campo. Si existe alguno, se llama durante la validación.</p>
<p>En concreto, el método clean_message() será llamado después de la lógica de validación por defecto para un campo determinado (en este caso, la lógica de validación para un CharField obligatorio).Debido a que los datos del campo ya han sido parcialmente procesados, hay que sacarlos de self.cleaned_data. Además, no tiene que preocuparse de comprobar que el valor existe y no es vacío, el validador lo hace de forma predeterminada.</p>
<p>Nosotros, utilizamos una combinación de len() y split() para contar el número de palabras. Si el usuario ha introducido muy pocas palabras, lanzamos un forms.ValidationError. La cadena adjunta a la excepción se muestra al usuario como un elemento de la lista de errores.</p>
<p>Es importante que devolvamos explícitamente el valor limpiado para el campo al final del método. Esto nos permite modificar el valor (o convertirlo a un tipo diferente de Python) dentro de nuestro método de validación personalizado. Si olvidamos de la instrucción de retorno, entonces será devuelto None y  el valor original se perderá.</p>
<h3>Especificar etiquetas</h3>
<p>Por defecto, las etiquetas del formulario HTML autogenerado por Django se crean mediante la sustitución de los guiones bajos por espacios y capitalizando la primera letra, por lo que la etiqueta para el campo e-mail es &#8220;E-mail&#8221;. ¿Suena familiar? Es el mismo algoritmo simple que los modelos Django utilizan para calcular los valores verbose_name de los campos por defecto.</p>
<p>Pero, como con los modelos Django, podemos personalizar la etiqueta de un campo determinado. Sólo tiene que utilizar la etiqueta, de este modo:</p>
<blockquote><p>
class ContactForm(forms.Form):</p>
<p style="padding-left:30px">subject = forms.CharField(max_length=100)<br />
	e-mail = forms.EmailField(required=False, label=&#8217;Your e-mail address&#8217;)<br />
	message = forms.CharField(widget=forms.Textarea)</p>
</blockquote>
<h3>Personalizar el diseño de los formularios</h3>
<p>Nuestra plantilla <em>contact_form.html</em> usa {{ form.as_table }} para mostrar el formulario, pero podemos visualizarlo de otras formas para conseguir un control más detallado sobre la pantalla.</p>
<p>La forma más rápida para personalizar la presentación de los formularios es con CSS. Las listas de error, en particular, podían realizarse con algunas mejoras visuales, y las listas de error autogeneradas usan &lt;ul class =  &#8220;errorlist&#8221;&gt; precisamente para que se pueda apuntar a ellas con CSS. El siguiente CSS hace realmente que nuestros errores destaquen:</p>
<blockquote><p>
&lt;style type=&#8221;text/css&#8221;&gt;</p>
<p>ul.errorlist {</p>
<p style="padding-left:30px">margin: 0;<br />
	padding: 0;</p>
<p>}</p>
<p>.errorlist li {</p>
<p style="padding-left:30px">background-color: red;<br />
	color: white;<br />
	display: block;<br />
	font-size: 10px;<br />
	margin: 0 0 3px;<br />
	padding: 4px 5px;</p>
<p>}</p>
<p>&lt;/style&gt;
</p></blockquote>
<p>Aunque es conveniente disponer de nuestro HTML de formulario generado para nosotros, en muchos casos, usted deseará reemplazar la representación por defecto.</p>
<p>Cada widget de campo (&lt;input type=&#8221;text&#8221;&gt;, &lt;select&gt;, &lt;textarea&gt;, etc) pueden ser renderizado individualmente mediante el acceso a {{ form.fieldname }} en la plantilla, y los errores asociados con un campo están disponibles como {{ form.fieldname.errors }}. Con esto en mente, podemos construir una plantilla personalizada para nuestro formulario de contacto con el siguiente código de plantilla:</p>
<blockquote><p>
&lt;html&gt;<br />
&lt;head&gt;</p>
<p style="padding-left:30px">&lt;title&gt;Contact us&lt;/title&gt;</p>
<p>&lt;/head&gt;<br />
&lt;body&gt;</p>
<p style="padding-left:30px">&lt;h1&gt;Contact us&lt;/h1&gt;<br />
	{% if form.errors %}</p>
<p style="padding-left:60px">&lt;p style=&#8221;color: red;&#8221;&gt;</p>
<p style="padding-left:90px">Please correct the error{{ form.errors|pluralize }} below.</p>
<p style="padding-left:60px">&lt;/p&gt;</p>
<p style="padding-left:30px">{% endif %}<br />
	&lt;form action=&#8221;" method=&#8221;post&#8221;&gt;</p>
<p style="padding-left:60px">&lt;div class=&#8221;field&#8221;&gt;</p>
<p style="padding-left:90px">{{ form.subject.errors }}<br />
			&lt;label for=&#8221;id_subject&#8221;&gt;Subject:&lt;/label&gt;<br />
			{{ form.subject }}</p>
<p style="padding-left:60px">&lt;/div&gt;</p>
<p>		&lt;div class=&#8221;field&#8221;&gt;</p>
<p style="padding-left:90px">{{ form.e-mail.errors }}<br />
			&lt;label for=&#8221;id_e-mail&#8221;&gt;Your e-mail address:&lt;/label&gt;<br />
			{{ form.e-mail }}</p>
<p style="padding-left:60px">&lt;/div&gt;</p>
<p>		&lt;div class=&#8221;field&#8221;&gt;</p>
<p style="padding-left:90px">{{ form.message.errors }}<br />
			&lt;label for=&#8221;id_message&#8221;>Message:&lt;/label&gt;<br />
			{{ form.message }}</p>
<p style="padding-left:60px">&lt;/div&gt;</p>
<p>		&lt;input type=&#8221;submit&#8221; value=&#8221;Submit&#8221;&gt;</p>
<p style="padding-left:30px">&lt;/form&gt;</p>
<p>&lt;/body&gt;<br />
&lt;/html&gt;
</p></blockquote>
<p>{{ form.message.errors }} muestra una &lt;ul class=&#8221;errorlist&#8221;&gt; si hay errores y una cadena en blanco si el campo es válido (o el formulario no está ligado). También puede tratar form.message.errors como un valor booleano o incluso iterar sobre él como una lista. Considere este ejemplo:</p>
<blockquote><p>
&lt;div class=&#8221;field{% if form.message.errors %} errors{% endif %}&#8221;&gt;</p>
<p style="padding-left:30px">{% if form.message.errors %}</p>
<p style="padding-left:60px">&lt;ul&gt;<br />
		{% for error in form.message.errors %}</p>
<p style="padding-left:90px">&lt;li>&lt;strong&gt;{{ error }}&lt;/strong&gt;&lt;/li&gt;</p>
<p style="padding-left:60px">{% endfor %}<br />
		&lt;/ul&gt;</p>
<p style="padding-left:30px">{% endif %}</p>
<p>	&lt;label for=&#8221;id_message&#8221;&gt;Message:&lt;/label&gt;<br />
	{{ form.message }}</p>
<p>&lt;/div&gt;
</p></blockquote>
<p>En el caso de errores de validación, esto añadirá una clase errors al &lt;div&gt; y muestra la lista de errores en una lista desordenada.</p>
<h3>Enlaces Relacionados:</h3>
<ul>
<li><a href="http://www.cibernatural.com/tutorial-de-django-i">Tutorial de Django I</a></li>
<li><a href="http://www.cibernatural.com/tutorial-de-django-ii">Tutorial de Django II</a></li>
<li><a href="http://www.cibernatural.com/tutorial-de-django-iii">Tutorial de Django III</a></li>
<li><a href="http://www.cibernatural.com/tutorial-de-django-iv">Tutorial de Django IV</a></li>
<li><a href="http://www.cibernatural.com/tutorial-de-django-v">Tutorial de Django V</a></li>
<li>Tutorial de Django VI</li>
</ul>

                            <div id="aspdf">
                                <a href="http://www.cibernatural.com/wp-content/plugins/as-pdf/generate.php?post=640">
                                    <span>Descargar en PDF</span>
                                </a>
                            </div>
                        ]]></content:encoded>
			<wfw:commentRss>http://www.cibernatural.com/tutorial-de-django-vi/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Web Corporativa de Transportes y Excavaciones Tiagua</title>
		<link>http://www.cibernatural.com/web-corporativa-de-transportes-y-excavaciones-tiagua/</link>
		<comments>http://www.cibernatural.com/web-corporativa-de-transportes-y-excavaciones-tiagua/#comments</comments>
		<pubDate>Mon, 05 Jul 2010 17:44:27 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Proyectos]]></category>
		<category><![CDATA[Construcción]]></category>
		<category><![CDATA[Lanzarote]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://www.cibernatural.com/?p=633</guid>
		<description><![CDATA[Cibernatural S.L. estrena la nueva web corporativa de la importante empresa del sector de la construcción en Lanzarote Transportes y Excavaciones Tiagua. http://www.excavacionestiagua.com En ella se pretende ir mostrando todos y cada uno de los productos y servicios que ofrece la empresa, entre los que destacan todo tipo de trabajos de Excavaciones, Movimiento de Tierras, [...]]]></description>
			<content:encoded><![CDATA[<p>Cibernatural S.L. estrena la nueva web corporativa de la importante empresa del sector de la construcción en Lanzarote Transportes y Excavaciones Tiagua.</p>
<p><a href="http://www.excavacionestiagua.com">http://www.excavacionestiagua.com</a></p>
<p>En ella se pretende ir mostrando todos y cada uno de los productos y servicios que ofrece la empresa, entre los que destacan todo tipo de trabajos de <strong>Excavaciones, Movimiento de Tierras, Demoliciones, Cimentaciones, etc.</strong>, así como de <strong>Transportes y Grúas</strong> para toda la isla de <strong>Lanzarote</strong>.</p>
<p>También, la página permite realizar comentarios de cada una de las noticias o novedades mostradas, con objeto de acercar al cliente o proveedor a la empresa, en cualquier momento y en cualquier lugar.</p>

                            <div id="aspdf">
                                <a href="http://www.cibernatural.com/wp-content/plugins/as-pdf/generate.php?post=633">
                                    <span>Descargar en PDF</span>
                                </a>
                            </div>
                        ]]></content:encoded>
			<wfw:commentRss>http://www.cibernatural.com/web-corporativa-de-transportes-y-excavaciones-tiagua/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nuevos portátiles DELL Latitude &#8211; Junio 2010</title>
		<link>http://www.cibernatural.com/nuevos-portatiles-dell-latitude-junio-2010/</link>
		<comments>http://www.cibernatural.com/nuevos-portatiles-dell-latitude-junio-2010/#comments</comments>
		<pubDate>Thu, 17 Jun 2010 16:12:00 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Venta]]></category>
		<category><![CDATA[DELL]]></category>
		<category><![CDATA[Hardware]]></category>

		<guid isPermaLink="false">http://www.cibernatural.com/?p=621</guid>
		<description><![CDATA[DELL lanza los nuevos portátiles de la serie Latitude™ E5410/E5510, E4310 y E2110. Dell lanza el nuevo Latitude™ E5410/E5510 Portátiles de altas prestaciones: consiga negocios de bajo coste Los portátiles Dell™ Latitude™ E5510 y E5410 ponen en sus manos una fiable tecnología informática portátil caracterizada por una mayor flexibilidad y una probada durabilidad, además de [...]]]></description>
			<content:encoded><![CDATA[<p>DELL lanza los nuevos portátiles de la serie Latitude™ E5410/E5510, E4310 y E2110.</p>
<p><br/><br/><br/></p>
<h3>Dell lanza el nuevo Latitude™ E5410/E5510</h3>
<h4>Portátiles de altas prestaciones: consiga negocios de bajo coste</h4>
<p><a href="http://www.cibernatural.com/wp-content/uploads/2010/06/latitudeE5410.jpg"><img src="http://www.cibernatural.com/wp-content/uploads/2010/06/latitudeE5410.jpg" alt="" title="latitudeE5410" width="337" height="338" class="alignleft size-full wp-image-624" /></a> </p>
<p>Los portátiles Dell™ Latitude™ E5510 y E5410 ponen en sus manos una fiable tecnología informática portátil caracterizada por una mayor flexibilidad y una probada durabilidad, además de una reducción del tiempo de implementación y las labores de mantenimiento.</p>
<p><strong>Resistencia inigualable</strong><br />
Todos los portátiles Latitude de la gama E se someten a rigurosas pruebas de durabilidad, lo que garantiza una vida útil prolongada. Esto se consigue gracias a su dureza y sofisticación, sus sólidas bisagras, sus resistentes pestillos, sus compactas pantallas reforzadas y la protección de datos críticos gracias al amortiguador de golpes Dell StrikeZone™.</p>
<p><strong>Fácil de usar</strong><br />
Revele a sus clientes cómo disparar la productividad gracias a las últimas opciones de conexión inalámbrica y una prolongada duración de la batería para poder colaborar prácticamente desde cualquier lugar. Gracias a los controles inteligentes, trabajar con estos sistemas resulta muy intuitivo; el teclado antimicrobiano, por su parte, reduce la proliferación de bacterias y hongos.</p>
<p><strong>Organización sencilla</strong><br />
Simplifique la infraestructura de tecnología informática de sus clientes con características como la estabilidad de los productos de esta gama, su intercompatibilidad, la asistencia global y un control de nivel empresarial; con todo ello, ahorrarán dinero y tiempo al departamento informático.</p>
<p><strong>Es hora de pasar a la acción</strong><br />
Póngase en marcha y empiece a sentar los sólidos cimientos de su negocio hoy mismo.</p>
<h3>Dell lanza el nuevo Latitude™ E4310</h3>
<h4>Un sistema ligero, aunque cargado de funciones</h4>
<p><a href="http://www.cibernatural.com/wp-content/uploads/2010/06/latitude4310.jpg"><img src="http://www.cibernatural.com/wp-content/uploads/2010/06/latitude4310.jpg" alt="" title="latitude4310" width="191" height="160" class="alignleft size-full wp-image-625" /></a></p>
<p>Este modelo de clase empresarial, que se encuentra disponible con una selección de procesadores Intel Core i5, proporciona al cliente un fantástico conjunto de ventajosas características con las que trabajar de un modo inteligente, productivo y organizado.</p>
<p><strong>Inteligencia</strong><br />
Si un ordenador está diseñado para utilizarse allí donde vaya el usuario, lo más sensato es fabricarlo para que cumpla totalmente dicho propósito. Por ello, al igual que con el resto de los portátiles Latitude de la gama E, se efectúan comprobaciones de la durabilidad del E4310 y se le somete a pruebas aceleradas de vida útil que emulan un uso realista del sistema. Un amortiguador de golpes Dell StrikeZone brinda una protección adicional a los datos del cliente en caso de caída del sistema (aun cuando el portátil esté apagado). Asimismo, una amplia gama de puertos y opciones de conexión posibilita múltiples formas de colaboración para poder trabajar sin que importe dónde.</p>
<p><strong>Productividad</strong><br />
Con el E4310, los clientes pueden aprestarse a trabajar con suma rapidez (bien individualmente, bien con compañeros vinculados a una red de portátiles). Sus clientes disfrutarán de una duración de la batería más prolongada, procesadores con alta eficiencia energética y la tecnología Latitude ON™, que permite comprobar el correo electrónico, el calendario y los contactos sin necesidad de iniciar el sistema (el tipo de funciones que ayudarán a sus clientes a ser más productivos).</p>
<p><strong>Organización</strong><br />
El nuevo Latitude E4310 puede contribuir a lograr una organización más eficiente de la infraestructura de tecnología informática de sus clientes haciendo hincapié en la estabilidad, la intercompatibilidad, la asistencia global y un control de nivel empresarial. Ahora, más que nunca, las organizaciones deben hacer frente a la necesidad de canalizar el dinero y el tiempo invertidos en tecnología informática hacia actividades productivas. El Latitude E4310 puede contribuir a este propósito reduciendo los gastos, el tiempo de implementación y las labores de mantenimiento.</p>
<h3>Dell lanza el nuevo Latitude™ E2110</h3>
<h4>Una hermosa fusión de forma, funcionalidad y diversión</h4>
<p><a href="http://www.cibernatural.com/wp-content/uploads/2010/06/latitude2110.jpg"><img src="http://www.cibernatural.com/wp-content/uploads/2010/06/latitude2110.jpg" alt="" title="latitude2110" width="191" height="170" class="alignleft size-full wp-image-626" /></a></p>
<p><strong>Funcionalidad inteligente</strong><br />
Aumente las oportunidades de aprendizaje en cualquier momento y lugar gracias a opciones integradas de banda ancha móvil. Incorpore una línea defensiva adicional frente a las bacterias con el teclado opcional a prueba de manipulaciones y con protección antimicrobiana.</p>
<p><strong>Auténtica flexibilidad</strong><br />
Satisfaga las necesidades de sus estudiantes con opciones como una pantalla táctil de alta definición o una cámara web opcional. Ponga en sus manos la capacidad de elegir entre la opción de batería de formato ligero o con una duración de hasta 9 horas y 27 minutos.</p>
<p><strong>Características básicas</strong><br />
El Dell Latitude 2110 constituye una asequible solución informática y de aprendizaje portátil que incorpora las características básicas de la gama Dell Latitude de orientación empresarial; gracias a ellas, los departamentos de tecnología informática logran ahorrar tiempo y dinero.</p>

                            <div id="aspdf">
                                <a href="http://www.cibernatural.com/wp-content/plugins/as-pdf/generate.php?post=621">
                                    <span>Descargar en PDF</span>
                                </a>
                            </div>
                        ]]></content:encoded>
			<wfw:commentRss>http://www.cibernatural.com/nuevos-portatiles-dell-latitude-junio-2010/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Proveedor de Bonos Tecnológicos del Gobierno de Canarias</title>
		<link>http://www.cibernatural.com/proveedor-de-bonos-tecnologicos-del-gobierno-de-canarias/</link>
		<comments>http://www.cibernatural.com/proveedor-de-bonos-tecnologicos-del-gobierno-de-canarias/#comments</comments>
		<pubDate>Tue, 18 May 2010 11:22:27 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Noticias]]></category>
		<category><![CDATA[Gobierno de Canarias]]></category>
		<category><![CDATA[Lanzarote]]></category>

		<guid isPermaLink="false">http://www.cibernatural.com/?p=611</guid>
		<description><![CDATA[Las Pymes que deseen abordar cualquier mejora en tecnologías de la información, podrán solicitar una ayuda económica a la Agencia Canaria de Investigación, Innovación y Sociedad de la Infromación (ACIISI), que se materializará en forma de bonos tecnológicos. Un bono tecnológico es el equivalente a una cantidad de dinero determinada destinada a cubrir una parte [...]]]></description>
			<content:encoded><![CDATA[<p>Las Pymes que deseen abordar cualquier mejora en tecnologías de la información, podrán solicitar una ayuda económica a la Agencia Canaria de Investigación, Innovación y Sociedad de la Infromación (ACIISI), que se materializará en forma de <strong>bonos tecnológicos</strong>. Un bono tecnológico es el equivalente a una cantidad de dinero determinada destinada a cubrir una parte del coste total del proyecto, cuyo beneficiario es la Pyme solicitante de la ayuda, pero que será abonada directamente por la ACIISI al prestador final de los servicios.</p>
<p>Cibernatural es nuevo <a href="http://www.gobiernodecanarias.org/aciisi/bonos/">proveedor de bonos tecnológicos del Gobierno de Canarias</a> para la isla de Lanzarote en la modalidad TIC2 &#8211; Desarrollo en implantación de innovaciones.<br />
Puede <a href="http://www.cibernatural.com/contacto/">contactar con Cibernatural</a> si quiere acogerse al programa con objeto de mejorar la infraestructura tecnológica de su empresa.</p>
<h3>Información completa del programa de subvenciones</h3>
<p><a href="http://www.gobiernodecanarias.org/aciisi/bonos/">http://www.gobiernodecanarias.org/aciisi/bonos/</a></p>

                            <div id="aspdf">
                                <a href="http://www.cibernatural.com/wp-content/plugins/as-pdf/generate.php?post=611">
                                    <span>Descargar en PDF</span>
                                </a>
                            </div>
                        ]]></content:encoded>
			<wfw:commentRss>http://www.cibernatural.com/proveedor-de-bonos-tecnologicos-del-gobierno-de-canarias/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tutorial de Django &#8211; V</title>
		<link>http://www.cibernatural.com/tutorial-de-django-v/</link>
		<comments>http://www.cibernatural.com/tutorial-de-django-v/#comments</comments>
		<pubDate>Wed, 12 May 2010 12:19:56 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Técnico]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://www.cibernatural.com/?p=602</guid>
		<description><![CDATA[El sitio de administración de Django Para una cierta clase de sitios Web, una interfaz de administración es una parte esencial de la infraestructura. Se trata de una interfaz basada en Web, limitada a los administradores del sitio de confianza, que permite la adición, la edición, y la eliminación de contenido del sitio. Los paquetes [...]]]></description>
			<content:encoded><![CDATA[<h2>El sitio de administración de Django</h2>
<p>Para una cierta clase de sitios Web, una interfaz de administración es una parte esencial de la infraestructura. Se trata de una interfaz basada en Web, limitada a los administradores del sitio de confianza, que permite la adición, la edición, y la eliminación de contenido del sitio.</p>
<h3>Los paquetes django.contrib</h3>
<p>La administración automática de Django es parte de una gran suite de funcionalidad de Django llamada django.contrib &#8211; la parte de código de Django que contiene diversas utilidades para el núcleo. Usted puede pensar en django.contrib como el equivalente de Django de la librería estándar de Python. Está integrado con Django de modo que no tenemos que reinventar la rueda en nuestras propias aplicaciones.</p>
<p>El sitio de administración se llama django.contrib.admin. Otras características disponibles en django.contrib incluyen un sistema de autenticación de usuarios (django.contrib.auth), soporte para sesiones anónimas (django.contrib.sessions), e incluso un sistema de comentarios de usuarios (django.contrib.comments).</p>
<h3>Activar la interfaz de administración</h3>
<p>El sitio de administración de Django es totalmente opcional, ya que sólo ciertos tipos de sitios necesitan esta funcionalidad. Eso significa que usted tendrá que dar unos pasos para activarlo en su proyecto.</p>
<p>En primer lugar, hacer unos cuantos cambios a su archivo de configuración: </p>
<ol>
<li>Añadir &#8216;django.contrib.admin&#8217; a la propiedad INSTALLED_APPS.</li>
<li>Asegúrese de que INSTALLED_APPS contiene &#8216;django.contrib.auth&#8217;, &#8216;django.contrib.contenttypes&#8217;, y &#8216;django.contrib.sessions&#8217;. El sitio de administración de Django requiere de estos tres paquetes.</li>
<li>Asegúrese de que MIDDLEWARE_CLASSES contiene los valores &#8216;django.middleware.common.CommonMiddleware&#8217;,<br />
&#8216;django.contrib.sessions.middleware.SessionMiddleware&#8217;, y &#8216;django.contrib.auth.middleware.AuthenticationMiddleware.</li>
</ol>
<p>En segundo lugar, ejecutar <strong>python manage.py syncdb</strong>. Este paso permitirá instalar las tablas extra de base de datos que utiliza el interfaz de administración. La primera vez que ejecute syncdb con &#8216;django.contrib.auth&#8217; en INSTALLED_APPS, se le preguntará sobre la creación de un super-usuario. Si usted no hace esto, tendrá que para ejecutar python manage.py createsuperuser por separado para crear una cuenta de usuario administrador, de lo contrario usted no será capaz de entrar en el sitio de administración.</p>
<p>En tercer lugar, agregue el sitio de administración a su URLconf (en <em>urls.py</em>, recuerde). Por defecto, el <em>urls.py</em> generado por <strong>django-admin.py startproject</strong> contiene el código comentado para la administración de Django, y todo lo que tiene que hacer es quitar los comentarios.</p>
<blockquote><p># Include these import statements&#8230;<br />
from django.contrib import admin<br />
admin.autodiscover()</p>
<p># And include this URLpattern&#8230;<br />
urlpatterns = patterns(&#8221;,</p>
<p style="padding-left:30px;"># &#8230;<br />
	(r&#8217;^admin/&#8217;, include(admin.site.urls)),<br />
	# &#8230;
</p>
<p>)</p></blockquote>
<p>Con esa poca configuración, ahora usted puede ver el sitio de administración de Django en acción.  Simplemente ejecute el servidor de desarrollo (<strong>python manage.py runserver</strong>) y visite <strong>http://127.0.0.1:8000/admin/</strong> en su navegador Web.</p>
<h3>Usar el sitio de administración</h3>
<p>Inicie sesión con el nombre de usuario y contraseña que agregó cuando creó el superusuario. Si usted no puede abrir una sesión, asegúrese de que ha creado realmente un super-usuario ejecutando <strong>python manage.py createsuperuser</strong>.</p>
<p>Una vez que esté conectado, lo primero que verá será la página de inicio de administración. Esta página muestra todos los tipos de datos disponibles que se pueden editar en el sitio de administración. En este punto, ya que no ha activado ninguno de nuestros modelos, la lista es escasa: sólo incluye Grupos y Usuarios, que son los dos modelos de administración por defecto editables.</p>
<p>Cada tipo de datos en el sitio de administración de Django tiene una lista de cambios y un formulario de edición. Las listas de cambios muestran todos los objetos disponibles en la base de datos, y los formularios de edición le permiten añadir, cambiar o eliminar registros particulares en su base de datos.</p>
<p>Haga clic en el vínculo Cambiar en la fila de los usuarios para cargar la página de lista de cambios de los usuarios. Esta página muestra todos los usuarios de la base de datos.</p>
<p>Pulse un nombre de usuario, y verá el formulario de edición de ese usuario. Esta página le permite cambiar los atributos del usuario.</p>
<h3>Añadir modelos al sitio de administración</h3>
<p>Vamos a añadir nuestros propios modelos a la página de administración a fin de que podemos agregar, modificar y eliminar objetos en nuestras tablas de bases de datos personalizadas utilizando esta interfaz agradable.</p>
<p>Vamos a seguir el ejemplo de los libros, donde definimos tres modelos: Publisher, Author, y Book.</p>
<p>En el directorio de libros (mysite/books), cree un archivo llamado <em>admin.py</em>, y escriba las siguientes líneas de código:</p>
<blockquote><p>from django.contrib import admin<br />
from mysite.books.models import Publisher, Author, Book</p>
<p>admin.site.register(Publisher)<br />
admin.site.register(Author)<br />
admin.site.register(Book)</p></blockquote>
<p>Este código le indica al sitio de administración de Django que ofrezca una interfaz para cada uno de estos modelos.</p>
<p>Una vez que haya hecho esto, vaya a la página de inicio de administración en su explorador Web (<strong>http://127.0.0.1:8000/admin/</strong>). Usted debe ver una sección de Libros con enlaces a los autores, libros y editores. Tendrá que reiniciar runserver para que los cambios tengan efecto.</p>
<p>Un aspecto digno de mencionar aquí es el manejo de las claves ajenas y las relaciones muchos-a-muchos por el sitio de administración, las cuales aparecen en el modelo Book.</p>
<p>En la página Add Book  del sitio de administración de Django (http://127.0.0.1:8000/admin/books/book/add/), el editor (una ForeignKey) es representado por una caja de selección, y el campo de los autores (un ManyToManyField) es representado por un cuadro de selección múltiple. Ambos campos están al lado de un signo más verde que te permite añadir los registros relacionados de ese tipo. Por ejemplo, si hace clic en el signo más verde junto al campo Publisher, obtendrá una ventana emergente que le permite añadir un editor.</p>
<h3>Cómo trabaja el sitio de administración</h3>
<p>Cuando Django carga su URLconf de <em>urls.py</em> al arrancar el servidor, se ejecuta la setencia admin.autodiscover() que añadimos como parte de la activación de la administración. Esta función itera sobre la configuración de INSTALLED_APPS y busca un archivo llamado <em>admin.py</em> en cada aplicación instalada. Si un <em>admin.py</em> existe en una aplicación dada, se ejecuta el código de ese fichero.</p>
<p>En el <em>admin.py</em> de nuestra aplicación books, cada llamada a admin.site.register() simplemente registra el modelo dado con la administración. El sitio de administración mostrará una interfaz de edición/modificación para cada modelo que se haya registrado de forma explícita.</p>
<p>La aplicación django.contrib.auth incluye su propio <em>admin.py</em>, por lo que usuarios y grupos se mostraron de forma automática en la administración.</p>
<p>El sitio de administración de Django es sólo una aplicación Django, con sus propios modelos, plantillas, vistas, y patrones URL. Usted puede examinar sus plantillas, vistas, y patrones URL hurgando en django / contrib / admin en su copia del código base de Django.</p>
<h3>Hacer campos opcionales</h3>
<p>Usted notará probablemente una limitación &#8211; los formularios de edición requieren que se rellenen todos los campos, mientras que en muchos casos le gustaría que determinados campos fuesen opcionales. Digamos, por ejemplo, que queremos que nuestro campo email del modelo Author sea opcional, es decir, que se permita una cadena en blanco. En el mundo real, usted no podría tener una dirección de correo electrónico en archivo para cada autor.</p>
<p>Para especificar que el campo de correo electrónico es opcional, modificar el modelo Author. Basta con añadir <strong>blank = True</strong> para el campo email, así:</p>
<blockquote><p>class Author(models.Model):</p>
<p style="padding-left:30px;">first_name = models.CharField(max_length=30)<br />
	last_name = models.CharField(max_length=40)<br />
	email = models.EmailField(blank=True)</p>
</blockquote>
<p>Al añadir blank = True, hemos comenzado la expansión de nuestro modelo más allá de una simple definición de la tabla de base de datos. Ahora, nuestra clase de modelo está empezando a convertirse en una rica colección de conocimiento sobre lo que los objetos Author son y qué pueden hacer. No sólo está el campo email representado por una columna VARCHAR en la base de datos, sino que es también un campo opcional en contextos como el sitio de administración de Django.</p>
<h3>Hacer a fechas y campos numéricos opcionales</h3>
<p>SQL tiene su propia forma de especificar los valores en blanco, un valor especial llamado NULL. NULL puede significar &#8220;desconocido&#8221; o &#8220;inválido&#8221;, o algún  otro significado específico de la aplicación. En SQL, un valor de NULL es diferente de una cadena vacía, al igual que el objeto especial de Python None es diferente de la una cadena vacía (&#8220;&#8221;).</p>
<p>Esto puede causar confusión y ambigüedad no deseados: ¿Por qué este disco tiene un valor NULL, pero este otro tiene una cadena vacía? ¿Hay una diferencia, o se introdujeron los datos de manera distinta?. Y ¿Cómo puedo obtener todos los registros que tienen un valor en blanco, debería buscar tanto los registros NULL como las cadenas vacías, o debo elegir sólo los que tienen cadenas vacías?</p>
<p>Para evitar esa ambigüedad, Django genera automáticamente sentencias CREATE TABLE añadiendo un valor explícito  NOT NULL a cada definición de columna.</p>
<p>En la mayoría de los casos, este comportamiento predeterminado es óptimo para su aplicación y le ahorrará dolores de cabeza por inconsistencia de los datos. Y funciona muy bien con el resto de Django, tales como el sitio de administración de Django, que inserta una cadena vacía (no un valor NULL) cuando se deja un campo de carácter en blanco.</p>
<p>Pero hay una excepción con los tipos de columna de base de datos que no aceptan cadenas vacías como valores válidos, tales como fechas, horas y números. Si intenta insertar una cadena vacía en una columna de fecha o número entero, lo más probable es obtener un error de base la de datos. En este caso, NULL es la única manera de especificar un valor vacío. En los modelos Django, usted puede especificar que se permite NULL añadiendo null = True a un campo.</p>
<p>En resumen, si desea permitir valores en blanco en un campo de fecha (por ejemplo, DateField, TimeField, DateTimeField) o campo numérico (por ejemplo, IntegerField, DecimalField, FloatField), tendrá que utilizar tanto <strong>null = True</strong> como <strong>blank = True</strong>.</p>
<p>Cambiemos nuestro modelo Book para permitir una fecha de publicación en blanco:</p>
<blockquote><p>class Book(models.Model):</p>
<p style="padding-left:30px;">title = models.CharField(max_length=100)<br />
	authors = models.ManyToManyField(Author)<br />
	publisher = models.ForeignKey(Publisher)<br />
	publication_date = models.DateField(blank=True, null=True)</p>
</blockquote>
<p>Agregar null = True es más complicado que añadir blank = True, porque null = True cambia la semántica de la base de datos, es decir, cambia la sentencia CREATE TABLE para eliminar el NOT NULL del campo publication_date. Para completar este cambio, <strong>necesitaremos actualizar la base de datos</strong>.</p>
<p>Por varias razones, Django no trata de automatizar los cambios en los esquemas de base de datos, por lo que es su propia responsabilidad ejecutar el ALTER TABLE adecuado siempre que se realice un cambio a un modelo. Recuerde que puede utilizar python manage.py dbshell para entrar en la shell de su servidor de base de datos. He aquí cómo quitar el NOT NULL en este caso en particular:</p>
<blockquote><p>ALTER TABLE books_book ALTER COLUMN publication_date DROP NOT NULL;</p></blockquote>
<p>Ahora el formulario de edición Add Book debería permitir valores de fecha de publicación vacíos.</p>
<h3>Personalizar etiquetas de campo</h3>
<p>En los formularios de edición del sitio de administración, la etiqueta de cada campo se genera a partir del nombre de campo del modelo. El algoritmo es simple: Django simplemente reemplaza los guiones bajos con espacios y coloca en mayúsculas el primer carácter. Así, por ejemplo, el campo publication_date del modelo Book tiene la fecha Publicación Date.</p>
<p>Sin embargo, los nombres de campo no siempre se prestan a etiquetas de campo agradables en administración, así que en algunos casos es posible que desee personalizar una etiqueta. Usted puede hacer esto especificando <strong>verbose_name</strong> en el campo del modelo adecuado. Por ejemplo, así es cómo podemos cambiar la etiqueta del campo Author.email a &#8220;e-mail&#8221;, con guión:</p>
<blockquote><p>class Author(models.Model):</p>
<p style="padding-left:30px;">first_name = models.CharField(max_length=30)<br />
	last_name = models.CharField(max_length=40)<br />
	email = models.EmailField(blank=True, verbose_name=&#8217;e-mail&#8217;)
</p>
</blockquote>
<p>Por último, tenga en cuenta que puede pasar el verbose_name como un argumento de posición, para una sintaxis más compacta. Este ejemplo es equivalente al anterior:</p>
<blockquote><p>class Author(models.Model):</p>
<p style="padding-left:30px;">first_name = models.CharField(max_length=30)<br />
	last_name = models.CharField(max_length=40)<br />
	email = models.EmailField(&#8216;e-mail&#8217;, blank=True)
</p>
</blockquote>
<p>Esto no funcionará con campos <em>ManyToManyField</em> o <em>ForeignKey</em>, debido a que requieren que el primer argumento sea una clase modelo. En esos casos, especificar verbose_name explícitamente. </p>
<h3>Personalizar clases ModelAdmin</h3>
<p>Los cambios que hemos hecho hasta ahora &#8211; blank = True, null = True, y verbose_name- son realmente cambios de nivel de modelo, no cambios de nivel de administración. Es decir, estos cambios son, fundamentalmente, una parte del modelo y sólo para ser utilizados por el sitio de administración, no hay nada específico de administración en ellos.</p>
<p>Más allá de estos, el sitio de administración de Django ofrece una gran cantidad de opciones que permiten personalizar la forma en que el sitio de administración trabaja para un modelo en particular. Tales opciones residen en clases <em>ModelAdmin</em>, que son clases que contienen la configuración de un modelo específico en una instancia  del sitio de administración específica.</p>
<h3>Personalizar listas de cambios</h3>
<p>Entremos en la personalización de administración, especificando los campos que se muestran en la lista de cambios para el modelo Author. De forma predeterminada, la lista de cambios muestra el resultado de <strong>__unicode__()</strong> para cada objeto.</p>
<p>Podemos mejorar este comportamiento predeterminado, añadiendo algunos campos más a la muestra de la lista de cambios. Sería útil, por ejemplo, ver cada e-mail del autor en esta lista, y sería bueno poder ordenarla por nombre y apellido.</p>
<p>Para que esto ocurra, vamos a definir una clase <em>ModelAdmin</em> para el modelo de Autor. Esta clase es la clave para personalizar la administración, y una de las cosas más básicas que le permite hacer es especificar la lista de campos a mostrar en las páginas de lista de cambios. Editar <em>admin.py</em> para realizar estos cambios:</p>
<blockquote><p>from django.contrib import admin<br />
from mysite.books.models import Publisher, Author, Book</p>
<p>class AuthorAdmin(admin.ModelAdmin):</p>
<p style="padding-left:30px;">list_display = (&#8216;first_name&#8217;, &#8216;last_name&#8217;, &#8216;email&#8217;)</p>
<p>admin.site.register(Publisher)<br />
admin.site.register(Author, AuthorAdmin)<br />
admin.site.register(Book)
</p></blockquote>
<p>Volver a cargar la página de lista de cambios del autor, y verá que ahora se muestran tres columnas: el nombre, el apellido y la dirección de correo electrónico. Además, cada una de esas columnas es ordenable haciendo clic en el encabezado de la columna.</p>
<p>Ahora agreguemos una barra de búsqueda simple. Añadir a <strong>search_fields</strong> a AuthorAdmin, así: </p>
<blockquote><p>class AuthorAdmin(admin.ModelAdmin):</p>
<p style="padding-left:30px;">list_display = (&#8216;first_name&#8217;, &#8216;last_name&#8217;, &#8216;email&#8217;)<br />
	search_fields = (&#8216;first_name&#8217;, &#8216;last_name&#8217;)
</p>
</blockquote>
<p>Actualice la página en su navegador, y debería ver una barra de búsqueda en la parte superior. Hemos incluido una barra de búsqueda que busca en los campos first_name y last_name. Ahora agreguemos algunos filtros de fecha para nuestra página de lista de cambios del modelo Book:</p>
<blockquote><p>from django.contrib import admin<br />
from mysite.books.models import Publisher, Author, Book</p>
<p>class AuthorAdmin(admin.ModelAdmin):</p>
<p style="padding-left:30px;">list_display = (&#8216;first_name&#8217;, &#8216;last_name&#8217;, &#8216;email&#8217;)<br />
	search_fields = (&#8216;first_name&#8217;, &#8216;last_name&#8217;)</p>
<p>class BookAdmin(admin.ModelAdmin):</p>
<p style="padding-left:30px;">list_display = (&#8216;title&#8217;, &#8216;publisher&#8217;, &#8216;publication_date&#8217;)<br />
	list_filter = (&#8216;publication_date&#8217;,)</p>
<p>admin.site.register(Publisher)<br />
admin.site.register(Author, AuthorAdmin)<br />
admin.site.register(Book, BookAdmin)
</p></blockquote>
<p>Hemos utilizado <strong>list_filter</strong>, que fija en una tupla los campos a utilizar para crear filtros en el lado derecho de la página de lista de cambios. Para los campos fecha, Django proporciona accesos directos para filtrar la lista por &#8220;Hoy&#8221;, &#8220;Últimos 7 días&#8221;, &#8220;Este mes&#8221;, y &#8220;Este año&#8221;.</p>
<p>list_filter también trabaja en campos de otros tipos, no sólo DateField. (Pruebe con los campos BooleanField y ForeignKey, por ejemplo.) Los filtros se muestran siempre que haya por lo menos dos valores a elegir.</p>
<p>Otra manera de ofrecer filtros de fecha es utilizar la opción de administración <strong>date_hierarchy</strong>, así: </p>
<blockquote><p>class BookAdmin(admin.ModelAdmin):</p>
<p style="padding-left:30px;">list_display = (&#8216;title&#8217;, &#8216;publisher&#8217;, &#8216;publication_date&#8217;)<br />
	list_filter = (&#8216;publication_date&#8217;,)<br />
	date_hierarchy = &#8216;publication_date&#8217;
</p>
</blockquote>
<p>Tenga en cuenta que date_hierarchy toma una cadena, no una tupla, porque sólo un campo de fecha puede utilizarse para la jerarquía.</p>
<p>Por último, vamos a cambiar el orden por defecto en que se muestran los libros en la página de lista de cambios a siempre ordenado descendente por la fecha de su publicación.</p>
<blockquote><p>class BookAdmin(admin.ModelAdmin):</p>
<p style="padding-left:30px;">list_display = (&#8216;title&#8217;, &#8216;publisher&#8217;, &#8216;publication_date&#8217;)<br />
	list_filter = (&#8216;publication_date&#8217;,)<br />
	date_hierarchy = &#8216;publication_date&#8217;<br />
	ordering = (&#8216;-publication_date&#8217;,)
</p>
</blockquote>
<p>Usando estas opciones, usted puede hacer una interfaz de edición de datos muy potente, lista para producción, con sólo unas pocas líneas de código.</p>
<h3>Personalizar los formularios de edición</h3>
<p>En primer lugar, vamos a personalizar la manera en que se ordenan los campos. De forma predeterminada, el orden de los campos en un formulario de edición corresponde al orden en que están definidas en el modelo. Nosotros podemos cambiar eso utilizando la opción <strong>fields</strong> en nuestra subclase <em>ModelAdmin</em>:</p>
<blockquote><p>class BookAdmin(admin.ModelAdmin):</p>
<p style="padding-left:30px;">list_display = (&#8216;title&#8217;, &#8216;publisher&#8217;, &#8216;publication_date&#8217;)<br />
	list_filter = (&#8216;publication_date&#8217;,)<br />
	date_hierarchy = &#8216;publication_date&#8217;<br />
	ordering = (&#8216;-publication_date&#8217;,)<br />
	fields = (&#8216;title&#8217;, &#8216;authors&#8217;, &#8216;publisher&#8217;, &#8216;publication_date&#8217;)
</p>
</blockquote>
<p>Otra cosa útil de la opción fields es que permite excluir a ciertos campos de la edición. Sólo dejando fuera los campo(s) que desea excluir. Por ejemplo, en nuestra base de datos de libros, se podría impedir que el campo publication_date fuese editable:</p>
<blockquote><p>class BookAdmin(admin.ModelAdmin):</p>
<p style="padding-left:30px;">list_display = (&#8216;title&#8217;, &#8216;publisher&#8217;, &#8216;publication_date&#8217;)<br />
	list_filter = (&#8216;publication_date&#8217;,)<br />
	date_hierarchy = &#8216;publication_date&#8217;<br />
	ordering = (&#8216;-publication_date&#8217;,)<br />
	fields = (&#8216;title&#8217;, &#8216;authors&#8217;, &#8216;publisher&#8217;)
</p>
</blockquote>
<p>Otro uso común de personalizar los formularios de edición tiene que ver con los campos muchos a muchos. El sitio de administración representa cada <em>ManyToManyField</em> como una caja de selección múltiple. Si desea seleccionar varios elementos, hay que mantener pulsada la tecla Control. El sitio de administración amablemente inserta un fragmento de texto que explica esto, pero, aún así, se hace difícil de manejar cuando su campo contiene cientos de opciones. La solución del sitio de administración es <strong>filter_horizontal</strong>.</p>
<blockquote><p>class BookAdmin(admin.ModelAdmin):</p>
<p style="padding-left:30px;">list_display = (&#8216;title&#8217;, &#8216;publisher&#8217;, &#8216;publication_date&#8217;)<br />
	list_filter = (&#8216;publication_date&#8217;,)<br />
	date_hierarchy = &#8216;publication_date&#8217;<br />
	ordering = (&#8216;-publication_date&#8217;,)<br />
	filter_horizontal = (&#8216;authors&#8217;,)
</p>
</blockquote>
<p>Recomendamos el uso filter_horizontal para cualquier ManyToManyField que tenga más de diez elementos. Es mucho más fácil de utilizar.También, recuerde que puede usar filter_horizontal en múltiples campos, especificando solamente cada nombre en la tupla. Las clases <em>ModelAdmin</em> también tienen una opción <strong>filter_vertical</strong>. Funciona exactamente como filter_horizontal, pero la interfaz coloca las dos casillas verticalmente en vez de horizontalmente. Es una cuestión de gusto personal.</p>
<p>filter_horizontal y filter_vertical funcionan sólo en los campos <em>ManyToManyField</em>, no en campos <em>ForeignKey</em>. Por defecto, el sitio de administración usa &lt;select&gt; simples para los campos <em>ForeignKey</em>, pero, como para <em>ManyToManyField</em>, a veces usted no quiere incurrir en la sobrecarga de tener que seleccionar todos los objetos relacionados con la pantalla en el menú desplegable. Por ejemplo, si nuestra base de datos de libros crece para incluir a miles de editores, el formulario Add Book podría tardar bastante en cargar, ya que tiene que cargar cada editor a mostrar en el cuadro &lt;select&gt;.</p>
<p>Puedes solucionar este problema con una opción llamada <strong>raw_id_fields</strong>. Ponga esto en una tupla de nombres de campo <em>ForeignKey</em>, y los campos se mostrarán en el administrador con una caja de entrada de texto simple (&lt;input type = &#8220;text&#8221;&gt;) en lugar de un &lt;select&gt;.</p>
<blockquote><p>class BookAdmin(admin.ModelAdmin):</p>
<p style="padding-left:30px;">list_display = (&#8216;title&#8217;, &#8216;publisher&#8217;, &#8216;publication_date&#8217;)<br />
	list_filter = (&#8216;publication_date&#8217;,)<br />
	date_hierarchy = &#8216;publication_date&#8217;<br />
	ordering = (&#8216;-publication_date&#8217;,)<br />
	filter_horizontal = (&#8216;authors&#8217;,)<br />
	raw_id_fields = (&#8216;publisher&#8217;,)
</p>
</blockquote>
<p>¿Qué introduciría en este cuadro de entrada? El identificador de la base de datos del editor.Teniendo en cuenta que los seres humanos normalmente no memorizan identificadores base de datos, hay un icono en el que puede hacer clic para iniciar una ventana desde la que puede seleccionar el editor.</p>
<h3>Usuarios, grupos y permisos</h3>
<p>Ya que está conectado como super-usuario, tiene acceso a crear, editar y eliminar cualquier objeto. Naturalmente, los diferentes entornos requieren de sistemas de autorización diferentes, no todos pueden o deben ser un superusuario. El sitio de administración de Django utiliza un sistema de permisos que se puede utilizar para dar acceso específico a los usuarios a las partes de la interfaz que necesitan.</p>
<p>Estas cuentas de usuario están destinadas a ser lo suficientemente generales como para utilizarse fuera de la interfaz de administración, pero las trataremos como cuentas de usuario de administración por ahora.</p>
<p>Puede editar los usuarios y los permisos a través de la interfaz de administración al igual que cualquier otro objeto. Los objetos de usuario tiene los campos nombre de usuario estándar, contraseña, e-mail, y nombre real, junto con un conjunto de campos que definen lo que el usuario puede hacer en la interfaz de administración. En primer lugar, hay un conjunto de 3 banderas boolean:</p>
<ul>
<li>El flag &#8220;activo&#8221; controla si el usuario está activo.</li>
<li>El flag &#8220;staff&#8221; controla si el usuario tiene permiso para acceder a la interfaz de administración.</li>
<li>El flag &#8220;superusuario&#8221; proporciona al usuario acceso completo a agregar, crear y eliminar cualquier elemento de la interfaz de administración.</li>
</ul>
<p>Los usuarios de administración &#8220;normales&#8221; &#8211; es decir, los miembros activos, no superusuarios &#8211; se les concede acceso de administración a través de los permisos asignados. Cada objeto editable a través de la interfaz de administración (por ejemplo, libros, autores, editores) tiene tres permisos: crear, editar y eliminar. La asignación de permisos a un usuario concede al usuario el correspondiente nivel de acceso. </p>
<p>Al crear un usuario, dicho usuario no tiene permisos. Por ejemplo, usted puede dar a un usuario permiso para agregar y cambiar los editores, pero no para eliminarlos. Tenga en cuenta que estos permisos se definen por el modelo, no por objeto, de forma que usted diga, &#8220;Juan puede hacer cambios en cualquier libro,&#8221; pero no le permite decir, &#8220;Juan puede hacer los cambios de cualquier libro publicado por Apress. &#8221;</p>
<p>También puede asignar usuarios a grupos. Un grupo es simplemente un conjunto de permisos que se aplican a todos los miembros de ese grupo. Los grupos son útiles para la concesión de permisos idénticos a un subconjunto de usuarios.</p>
<h3>Enlaces Relacionados:</h3>
<ul>
<li><a href="http://www.cibernatural.com/tutorial-de-django-i">Tutorial de Django I</a></li>
<li><a href="http://www.cibernatural.com/tutorial-de-django-ii">Tutorial de Django II</a></li>
<li><a href="http://www.cibernatural.com/tutorial-de-django-iii">Tutorial de Django III</a></li>
<li><a href="http://www.cibernatural.com/tutorial-de-django-iv">Tutorial de Django IV</a></li>
<li>Tutorial de Django V</li>
<li><a href="http://www.cibernatural.com/tutorial-de-django-vi">Tutorial de Django VI</a></li>
</ul>

                            <div id="aspdf">
                                <a href="http://www.cibernatural.com/wp-content/plugins/as-pdf/generate.php?post=602">
                                    <span>Descargar en PDF</span>
                                </a>
                            </div>
                        ]]></content:encoded>
			<wfw:commentRss>http://www.cibernatural.com/tutorial-de-django-v/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Últimas oportunidades en portátiles DELL &#8211; Abril 2010</title>
		<link>http://www.cibernatural.com/ultimas-oportunidades-en-portatiles-dell-abril-2010/</link>
		<comments>http://www.cibernatural.com/ultimas-oportunidades-en-portatiles-dell-abril-2010/#comments</comments>
		<pubDate>Thu, 29 Apr 2010 11:13:20 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Venta]]></category>
		<category><![CDATA[DELL]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Lanzarote]]></category>

		<guid isPermaLink="false">http://www.cibernatural.com/?p=592</guid>
		<description><![CDATA[Este nuevo boletín presenta una nueva ola de portátiles y sus avances en potencia, durabilidad, movilidad, productividad, etc. Disfrute de su lectura y de las nuevas oportunidades para desarrollar su negocio. Latitude™ E6410/E6510 y E6410 ATG Una nueva dimensión de libertad en el trabajo Los distintos entornos de trabajo requieren distintos ordenadores y la gama [...]]]></description>
			<content:encoded><![CDATA[<p>Este nuevo boletín presenta una nueva ola de portátiles y sus avances en potencia, durabilidad, movilidad, productividad, etc. Disfrute de su lectura y de las nuevas oportunidades para desarrollar su negocio.</p>
<p><br/><br/></p>
<h3>Latitude™ E6410/E6510 y E6410 ATG</h3>
<p><a href="http://www.cibernatural.com/wp-content/uploads/2010/04/latitudeE6410.jpg"><img src="http://www.cibernatural.com/wp-content/uploads/2010/04/latitudeE6410.jpg" alt="" title="latitudeE6410" width="190" height="239" class="alignleft size-full wp-image-596" /></a><br />
<strong>Una nueva dimensión de libertad en el trabajo</strong></p>
<p>Los distintos entornos de trabajo requieren distintos ordenadores y la gama de portátiles Dell Latitude™ aumenta sus posibilidades empresariales con el control de clase empresarial de los servicios informáticos, el diseño y la productividad para satisfacer casi cualquier necesidad de informática portátil profesional. </p>
<p><strong>Diseño fiable</strong></p>
<ul>
<li>Compacto, ligero y duradero gracias al diseño Dell TriMetal™, a Dell StrikeZone™ y al sensor de caída de respuesta rápida</li>
</ul>
<p><strong>Productividad inteligente</strong></p>
<ul>
<li>Nuevas CPU Intel Core™ i5 e i7 con la tecnología Turbo Boost, memoria DDR3 y gráficos actualizados</li>
<li>Cámara web de alta definición, micrófono digital y altavoces mejorados para una mejor experiencia de colaboración</li>
<li>Movilidad optimizada gracias a las baterías de mayor duración</li>
</ul>
<p><strong>Control de clase empresarial</strong></p>
<ul>
<li>Gama compatible entre sí con estaciones base comunes, periféricos intercambiables y adaptadores de CA compatibles</li>
<li>Nueva batería de 3 años con garantía de que mantiene la vida óptima de la batería durante más tiempo</li>
</ul>
<h3>Presentación del nuevo Dell Precision M4500</h3>
<p><a href="http://www.cibernatural.com/wp-content/uploads/2010/04/precisionM4500.jpg"><img src="http://www.cibernatural.com/wp-content/uploads/2010/04/precisionM4500.jpg" alt="" title="precisionM4500" width="190" height="235" class="alignleft size-full wp-image-597" /></a><strong>Potencia cuando y donde la necesita</strong></p>
<p>¿Sus clientes desean maximizar el rendimiento? El nuevo Dell Precision M4500 permite a los ingenieros y a los usuarios creativos ser productivos en cualquier parte. Dell ha incluido la máxima capacidad de rendimiento posible que se puede dar en una estación de trabajo portátil de 15&#8243;. También incluye más recursos para las aplicaciones con la certificación ISV y un diseño que permite gestionar las aplicaciones más exigentes para los usuarios finales creativos y profesionales más minuciosos.</p>
<p><strong>¿Qué otras ventajas le ofrece el Dell Precision M4500?</strong></p>
<ul>
<li>Movilidad y gran capacidad de ampliación. La estación de trabajo portátil de 15” con gran capacidad de ampliación innova con las últimas opciones de almacenamiento para satisfacer las necesidades de los usuarios que llevan enormes cantidades de datos en sus portátiles.</li>
<li>Control total de la propiedad. Permite que las estaciones de trabajo portátiles sean tan seguras y fáciles de gestionar y de mantener como cualquier otro cliente comercial.</li>
<li>Certificación de proveedores de software independientes para 95 aplicaciones de 35 proveedores clave del sector.</li>
<li>Procesadores Intel® Core™ i5, Core™ i7 y Core™ i7 de núcleo cuádruple Extreme Edition para obtener un rendimiento excepcional.</li>
<li>Gráficos avanzados en 3D con los motores de gráficos NVIDIA Quadro® FX 880M o Quadro® FX 1800M con 1 GB de memoria gráfica dedicada para las tareas más exigentes, como por ejemplo la edición de vídeo, animaciones y CAD.</li>
<li>Memoria de hasta 8 GB con 2 ranuras DIMM que permite que las aplicaciones que requieren gran cantidad de memoria funcionen sin problemas.</li>
<li>Dos unidades de almacenamiento (un disco duro y una minitarjeta SSD) con una capacidad de almacenamiento de hasta 500 GB.</li>
</ul>
<p>Estas son sólo algunas de las ventajas de Dell Precision M4500.</p>

                            <div id="aspdf">
                                <a href="http://www.cibernatural.com/wp-content/plugins/as-pdf/generate.php?post=592">
                                    <span>Descargar en PDF</span>
                                </a>
                            </div>
                        ]]></content:encoded>
			<wfw:commentRss>http://www.cibernatural.com/ultimas-oportunidades-en-portatiles-dell-abril-2010/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Optimización de motores de búsqueda de Agentes Comerciales de Gestión</title>
		<link>http://www.cibernatural.com/optimizacion-de-motores-de-busqueda-de-agentes-comerciales-de-gestion/</link>
		<comments>http://www.cibernatural.com/optimizacion-de-motores-de-busqueda-de-agentes-comerciales-de-gestion/#comments</comments>
		<pubDate>Thu, 22 Apr 2010 10:59:45 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Noticias]]></category>
		<category><![CDATA[Canarias]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Las Palmas]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://www.cibernatural.com/?p=583</guid>
		<description><![CDATA[La empresa de Representación Comercial en Canarias ACG Representaciones, con objeto de mejorar el posicionamiento en buscadores, ha vuelto a confiar en Cibernatural para realizar el SEO (Optimización de motores de búsqueda) a la web corporativa de la empresa. Se han realizado múltiples mejoras, entre las cuales destacan: Mejoras en la indexación del contenido y [...]]]></description>
			<content:encoded><![CDATA[<p>La empresa de <a href="http://www.agentescomercialesdegestion.com">Representación Comercial en Canarias ACG Representaciones</a>, con objeto de mejorar el posicionamiento en buscadores, ha vuelto a confiar en Cibernatural para realizar el SEO (Optimización de motores de búsqueda) a la web corporativa de la empresa.</p>
<p>Se han realizado múltiples mejoras, entre las cuales destacan:</p>
<ul>
<li>Mejoras en la indexación del contenido y de los elementos gráficos de las páginas</li>
<li>Aumento de la indexabilidad en los sistemas de navegación del sitio web</li>
<li>Alta y notificación de modificaciones en buscadores genéricos</li>
<li>Generación de etiquetas</li>
</ul>
<h3>Enlaces Relacionados</h3>
<ul>
<li><a href="http://www.agentescomercialesdegestion.com">http://www.agentescomercialesdegestion.com</a></li>
</ul>

                            <div id="aspdf">
                                <a href="http://www.cibernatural.com/wp-content/plugins/as-pdf/generate.php?post=583">
                                    <span>Descargar en PDF</span>
                                </a>
                            </div>
                        ]]></content:encoded>
			<wfw:commentRss>http://www.cibernatural.com/optimizacion-de-motores-de-busqueda-de-agentes-comerciales-de-gestion/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
