I would like to send HTTP post request by using
HTTP::Client#post(path, headers : HTTP::Headers | ::Nil = nil, *, form : Hash(String, String) | NamedTuple)
I try to do this
url = "https://api.authy.com/protected/json/phones/verification/start"
headers = HTTP::Headers{"X-Authy-API-Key" => api_key}
form = {
via: "sms",
country_code: country_code,
phone_number: phone_number,
code_length: 6,
locale: "ru",
}.to_h
response = HTTP::Client.post(url, headers: headers, form: form)
Unfortunately, I got compilation error
no argument named 'form'
Matches are:
- HTTP::Client#post(path, headers : HTTP::Headers | ::Nil = nil, body : BodyType = nil) (trying this one)
- HTTP::Client#post(path, headers : HTTP::Headers | ::Nil = nil, body : BodyType = nil, &block)
- HTTP::Client#post(path, headers : HTTP::Headers | ::Nil = nil, *, form : String | IO)
- HTTP::Client#post(path, headers : HTTP::Headers | ::Nil = nil, *, form : String | IO, &block)
- HTTP::Client#post(path, headers : HTTP::Headers | ::Nil = nil, *, form : Hash(String, String) | NamedTuple)
- HTTP::Client#post(path, headers : HTTP::Headers | ::Nil = nil, *, form : Hash(String, String) | NamedTuple, &block)
What is correct way to do it?
This compilation error happens because .to_h returns a Hash(Symbol, Int32 | String) on your named tuple, and that is incompatible with any of the definitions for HTTP::Client.post.
To solve this, I suggest to explicitly define form as a Hash(String, String) and replace the keys and values by their string representation:
form : Hash(String, String) = {
"via" => "sms",
"country_code" => country_code.to_s, # assuming this is not a string
"phone_number" => phone_number,
"code_length" => "6",
"locale" => "ru",
}
Related
Background:
I have some code (Rust) that finds (Regex) matches and assigns the found values to fields in a struct named Article (where all fields are of type String):
pub struct Article {
// user facing data
title: String,
category: String,
subcategory: String,
genre: String,
published: String,
estimated_read_time: String,
description: String,
tags: String,
keywords: String,
image: String,
artwork_credit: String,
// meta data
metas: String,
// location
path: String,
slug: String,
// file data
content: String
}
A regular expression ("//\- define (.*?): (.*?)\n") is used to extract comments from the article's template that define data for that article:
// iterate through HTML property pattern matches
for capture in re_define.captures_iter(&file_content as &str) {
// remove the declaration from the the HTML output
article_content = article_content.replace(&capture[0].to_string(), "");
// get the property value
let property_value: &String = &capture[2].to_string();
// determine what field to assign the property to and assign it
match capture[1].to_lowercase().as_str() {
"title" => article.title = property_value.clone(),
"category" => article.category = property_value.clone(),
"subcategory" => article.subcategory = property_value.clone(),
"genre" => article.genre = property_value.clone(),
"published" => article.published = property_value.clone(),
"estimated_read_time" => article.estimated_read_time = property_value.clone(),
"description" => article.description = property_value.clone(),
"tags" => article.tags = property_value.clone(),
"keywords" => article.keywords = property_value.clone(),
"image" => article.image = property_value.clone(),
unknown_property # _ => {
println!("Ignoring unknown property: {}", &unknown_property);
}
}
}
Note: article is an instance of Article.
Issue:
The code works but what I'm concerned about the following part:
"title" => article.title = property_value.clone(),
"category" => article.category = property_value.clone(),
"subcategory" => article.subcategory = property_value.clone(),
"genre" => article.genre = property_value.clone(),
"published" => article.published = property_value.clone(),
"estimated_read_time" => article.estimated_read_time = property_value.clone(),
"description" => article.description = property_value.clone(),
"tags" => article.tags = property_value.clone(),
"keywords" => article.keywords = property_value.clone(),
"image" => article.image = property_value.clone(),
It calls .clone() on the same String (property_value) for every match (10 matches per article template), for every article template (a couple dozen templates in total), and I don't think it's the most efficient way to do it.
Note: I'm not sure if match is cloning for non-matches.
What I tried:
I tried referencing the property_value String, but I got an error for each reference.
Error from IDE (VS Code):
mismatched types
expected struct `std::string::String`, found `&&std::string::String`
expected due to the type of this binding
try using a conversion method: `(`, `).to_string()`
Error from cargo check:
error[E0308]: mismatched types
--> src/article.rs:84:38
|
84 | "image" => article.image = &property_value,
| ------------- ^^^^^^^^^^^^^^^ expected struct `std::string::String`, found `&&std::string::String`
| |
| expected due to the type of this binding
|
help: try using a conversion method
|
84 | "image" => article.image = (&property_value).to_string(),
| + +++++++++++++
I did try using .to_string(), but I'm not sure if converting a String to the same type is the most efficient to do it either.
Question:
How do I avoid calling .clone() on property_value so many times?
Going by the types, you should just be able to drop the borrow in property_value and then you don't need the .clone()s.
let property_value: &String = &capture[2].to_string();
// change to
let property_value: String = capture[2].to_string();
// or just simply
let property_value = capture[2].to_string();
I'm assuming this was added as capture[2] returns a str (non-sized type) which would require the & but with to_string() it converts to the owned type String which is fine on it's own. This wont have any performance effect as to_string() copies anyway.
I have this class:
import scala.util.parsing.combinator.JavaTokenParsers
class RequestMappingParser extends JavaTokenParsers {
def key: Parser[String] = "value" | "method" | "consumes" | "params"
def singleValue: Parser[String] = """[^),]*""".r
def multipleValues: Parser[String] = "{" ~ repsep(ident, ",") ~ "}" ^^ {
case "{" ~ lst ~ "}" => lst.mkString(", ")
}
def value: Parser[String] = multipleValues | singleValue
def keyValue: Parser[(String, String)] = (key ~ "=" ~ value).map {
case k ~ _ ~ v => k -> v
}
def commaDelimitedSeq: Parser[Map[String, String]] = repsep(keyValue, ",").map(_.toMap)
def requestMapping: Parser[MethodRequestMapping] = ("#RequestMapping(" ~ commaDelimitedSeq ~ ")").map {
case _ ~ map ~ _ =>
val consumes = if (map.contains("consumes")) Some(map("consumes")) else None
val value = if (map.contains("value")) Some(map("value")) else None
val method = if (map.contains("method")) Some(map("method")) else None
val params = if (map.contains("params")) Some(map("params")) else None
new MethodRequestMapping(value = value, method = method, consumes = consumes, params = params)
}
}
I have this test:
test("#RequestMapping – params") {
val parser = new RequestMappingParser()
val requestMappingStr = """#RequestMapping(
| value = "/ex/bars",
| params = { "id", "second" },
| method = GET)""".stripMargin
val parseResult = parser.parse(parser.requestMapping, requestMappingStr)
val result = parseResult.get
assert(result.value.get.equals("\"/ex/bars\""))
assert(result.method.get.equals("GET"))
assert(result.params.get.equals("{ \"id\", \"second\" }"))
}
But the test is failing with this parsing error:
[3.18] failure: ')' expected but ',' found
params = { "id", "second" },
^
I am using Scala 2.13. What do I have wrong here?
import scala.util.parsing.combinator.JavaTokenParsers
class RequestMappingParser extends JavaTokenParsers {
def key: Parser[String] = "value" | "method" | "consumes" | "params"
def singleValue: Parser[String] = """[^),]*""".r
def multipleValues: Parser[String] = "{" ~ repsep(stringLiteral, ",") ~ "}" ^^ {
case "{" ~ lst ~ "}" => "{ " + lst.mkString(", ") + " }"
}
def value: Parser[String] = multipleValues | singleValue
def keyValue: Parser[(String, String)] = (key ~ "=" ~ value).map {
case k ~ _ ~ v => k -> v
}
def commaDelimitedSeq: Parser[Map[String, String]] = repsep(keyValue, ",").map(_.toMap)
def requestMapping: Parser[MethodRequestMapping] = ("#RequestMapping(" ~ commaDelimitedSeq ~ ")").map {
case _ ~ map ~ _ =>
val consumes = if (map.contains("consumes")) Some(map("consumes")) else None
val value = if (map.contains("value")) Some(map("value")) else None
val method = if (map.contains("method")) Some(map("method")) else None
val params = if (map.contains("params")) Some(map("params")) else None
new MethodRequestMapping(value = value, method = method, consumes = consumes, params = params)
}
}
Parsing case statements in scala
CASE WHEN col1 <> 0 AND col2 <> 0 THEN 'COL1 & COL2 IS NOT ZERO' ELSE 'COL1 & COL2 IS ZERO'
challenge here is to give all the scenarios where case statement can come for e.g. it can come inside a function. Also case statements/functions etc. can come inside another case statements which has to be handled.
This problem can be solved with scala parser combinator
first define the classes needed to map experssions
sealed trait Exp {
def asStr: String
override def toString: String = asStr
}
case class OperationExp(a: Exp, op: String, b: Exp, c: Option[String]) extends Exp { override def asStr = s"$a $op $b ${c.getOrElse("")}" }
case class CaseConditions(conditionValue: List[(String, String)] , elseValue: String, asAlias: Option[Exp]) extends Exp {
override def asStr = "CASE " + conditionValue.map(c => s"WHEN ${c._1} THEN ${c._2}").mkString(" ") + s" ELSE ${elseValue} END ${asAlias.getOrElse("")}"
}
now the solution
case class OperationExp(a: Exp, op: String, b: Exp, c: Option[String]) extends Exp { override def asStr = s"$a $op $b ${c.getOrElse("")}" }
case class CaseConditions(conditionValue: List[(String, String)] , elseValue: String, asAlias: Option[Exp]) extends Exp {
override def asStr = "CASE " + conditionValue.map(c => s"WHEN ${c._1} THEN ${c._2}").mkString(" ") + s" ELSE ${elseValue} END ${asAlias.getOrElse("")}"
}
val identifiers: Parser[String] = "[a-zA-Z0-9_~\\|,'\\-\\+:.()]+".r
val operatorTokens: Parser[String] = "[<>=!]+".r | ("IS NOT" | "IN" | "IS")
val conditionJoiner: Parser[String] = ( "AND" | "OR" )
val excludeKeywords = List("CASE","WHEN", "THEN", "ELSE", "END")
val identifierWithoutCaseKw: Parser[Exp] = Parser(input =>
identifiers(input).filterWithError(
!excludeKeywords.contains(_),
reservedWord => s"$reservedWord encountered",
input
)
) ^^ StrExp
val anyStrExp: Parser[Exp] = "[^()]*".r ^^ StrExp
val funcIdentifier: Parser[Exp] = name ~ ("(" ~> (caseConditionExpresionParser | funcIdentifier | anyStrExp) <~ ")") ^^ {case func ~ param => FunCallExp(func, Seq(param))}
val identifierOrFunctions = funcIdentifier | identifierWithoutCaseKw
val conditionParser: Parser[String] =
identifierOrFunctions ~ operatorTokens ~ identifierOrFunctions ~ opt(conditionJoiner) ^^ {
case a ~ op ~ b ~ c => s"$a $op $b ${c.getOrElse("")}"
}
def caseConditionExpresionParser: Parser[CaseConditions] = "CASE" ~ rep1("WHEN" ~ rep(conditionParser) ~ "THEN" ~ rep(identifierWithoutCaseKw)) ~ "ELSE" ~ rep(identifierWithoutCaseKw) ~ "END" ~ opt("AS" ~> identifierWithoutCaseKw)^^ {
case "CASE" ~ conditionValuePair ~ "ELSE" ~ falseValue ~ "END" ~ asName =>
CaseConditions(
conditionValuePair.map(cv => (
cv._1._1._2.mkString(" "),
parsePipes(cv._2.mkString(" ")).isRight match {
case true => parsePipes(cv._2.mkString(" ")).right.get
case _ => cv._2.mkString(" ")
}
)),
parsePipes(falseValue.mkString("")).isRight match {
case true => parsePipes(falseValue.mkString(" ")).right.get
case _ => falseValue.mkString("")
}, asName)
}
//this parser can be used to get the results
val caseExpression = caseConditionExpresionParser | funcIdentifier
def parsePipes(input: String): Either[Seq[ParsingError], String] = {
parse(caseExpression, input) match {
case Success(parsed, _) => Right(parsed.asStr)
case Failure(msg, next) => Left(Seq(ParsingError(s"Failed to parse $pipedStr: $msg, next: ${next.source}.")))
case Error(msg, next) => Left(Seq(ParsingError(s"Error in $pipedStr parse: $msg, next: ${next.source}.")))
}
}
How to write right test, in order to test the below csv data stored in database table. In the input other than item, anything could be optional.
In this, item is key, rest all goes as part of json format typically it looks like this in database {"brand": "Brand6", "category": "Category6", "subcategory": "Sub-Category6"}
Input:
item,category,subcategory,brand,type,feature
TEST-ITEM6,Category6,Sub-Category6,Brand6
TEST-ITEM7,Category7,Sub-Category7,Brand7,TYPE7,FEATURE7
TEST-ITEM8,Category8,Sub-Category8,Brand8,TYPE8,FEATURE8
Test case tried:
def "Case 3a. Verify New 2 records with two more additional fields along with earlier fields to the same tenant"() {
expect:
sql().eachRow("SELECT * FROM item WHERE item IN ('"+item+"')") { row ->
def dbItem = row[0]
def dbAttributes = getJsonToObject(row[1])
def dbCategory = dbAttributes.getAt("category").toString()
def dbSubCategory = dbAttributes.getAt("subcategory").toString()
def dbBrand = dbAttributes.getAt("brand").toString()
def dbType = dbAttributes.getAt("type").toString()
def dbFeature = dbAttributes.getAt("feature").toString()
assert dbItem == item
assert category == dbCategory
assert subcategory == dbSubCategory
assert brand == dbBrand
assert type == dbType
assert feature == dbFeature
}
where:
item << ['TEST-ITEM6', 'TEST-ITEM7', 'TEST-ITEM8']
category << ['Category6','Category7', 'Category8']
subcategory << ['Sub-Category6','Sub-Category7', 'Sub-Category8']
brand << ['Brand6','Brand7', 'Brand8']
type << ['TYPE7', 'TYPE8']
feature << ['FEATURE7', 'FEATURE8']
}
Error:
Condition not satisfied:
type == dbType
| | |
TYPE8| TYPE7
false
1 difference (80% similarity)
TYPE(8)
TYPE(7)
Expected :TYPE7
Actual :TYPE8
In this case I would recommend to use Data Tables as it becomes more readable and resembles your input more closely.
And while type and feature are optional, you need to provide some value for it. It could be null or it could be an empty List or Map (if an Item can have more than one type/feature)
So you where block might look like this:
item | category | subcategory | brand | typeFeatureMap
'TEST-ITEM6' | 'Category6' | 'Sub-Category6' | 'Brand6' | [:] // empty
'TEST-ITEM7' | 'Category7' | 'Sub-Category7' | 'Brand7' | ['TYPE7':'FEATURE7']
'TEST-ITEM8' | 'Category8' | 'Sub-Category8' | 'Brand8' | ['TYPE8':'FEATURE8']
I would also recommend to collect the data and then compare it, so you get around ordering issues.
So bofore your eachRow do something like
def itemFeatures = [:]
In your eachRow do something like
itemFeatures.put(dbAttributes.getAt("type").toString(), dbAttributes.getAt("feature").toString())
And afterwards
itemFeatures == typeFeatureMap
While not answering your question, I would recommend to think about separating the tests from your database if possible.
If you create separate tests for an database abstraction layer and your business logic, you'll be more happy in the long run ;)
For the optional fields you can use the Elvis operator ?: like this (sorry, long code, I modeled your database and two new test cases, one with many optional fields and one failing test):
package de.scrum_master.stackoverflow
import spock.lang.Specification
import spock.lang.Unroll
class DataTableWithOptionalItemsTest extends Specification {
#Unroll
def "Case 3a. Verify record '#item' with possibly optional fields"() {
expect:
testData[item].each { row ->
def dbItem = row["item"]
def dbCategory = row["category"]
def dbSubCategory = row["subcategory"]
def dbBrand = row["brand"]
def dbType = row["type"]
def dbFeature = row["feature"]
assert dbItem == item
assert (category ?: dbCategory) == dbCategory
assert (subcategory ?: dbSubCategory) == dbSubCategory
assert (brand ?: dbBrand) == dbBrand
assert (type ?: dbType) == dbType
assert (feature ?: dbFeature) == dbFeature
}
where:
item | category | subcategory | brand | type | feature
'TEST-ITEM6' | 'Category6' | 'Sub-Category6' | 'Brand6' | null | null
'TEST-ITEM7' | 'Category7' | 'Sub-Category7' | 'Brand7' | 'TYPE7' | 'FEATURE7'
'TEST-ITEM8' | 'Category8' | 'Sub-Category8' | 'Brand8' | 'TYPE8' | 'FEATURE8'
'TEST-ITEM9' | null | null | null | null | null
'TEST-FAIL' | 'CategoryX' | 'Sub-CategoryX' | 'BrandX' | 'TYPEX' | 'FEATUREX'
}
static final testData = [
'TEST-ITEM6': [
[
item : 'TEST-ITEM6',
category : 'Category6',
subcategory: 'Sub-Category6',
brand : 'Brand6',
type : 'dummy',
feature : null
],
[
item : 'TEST-ITEM6',
category : 'Category6',
subcategory: 'Sub-Category6',
brand : 'Brand6',
type : null,
feature : "foo"
]
],
'TEST-ITEM7': [
[
item : 'TEST-ITEM7',
category : 'Category7',
subcategory: 'Sub-Category7',
brand : 'Brand7',
type : 'TYPE7',
feature : 'FEATURE7'
],
[
item : 'TEST-ITEM7',
category : 'Category7',
subcategory: 'Sub-Category7',
brand : 'Brand7',
type : 'TYPE7',
feature : 'FEATURE7'
]
],
'TEST-ITEM8': [
[
item : 'TEST-ITEM8',
category : 'Category8',
subcategory: 'Sub-Category8',
brand : 'Brand8',
type : 'TYPE8',
feature : 'FEATURE8'
],
[
item : 'TEST-ITEM8',
category : 'Category8',
subcategory: 'Sub-Category8',
brand : 'Brand8',
type : 'TYPE8',
feature : 'FEATURE8'
]
],
'TEST-ITEM9': [
[
item : 'TEST-ITEM9',
category : 'Category1',
subcategory: 'Sub-Category1',
brand : 'Brand1',
type : 'TYPE1',
feature : 'FEATURE1'
],
[
item : 'TEST-ITEM9',
category : null,
subcategory: null,
brand : null,
type : null,
feature : null
]
],
'TEST-FAIL' : [
[
item : 'TEST-FAIL',
category : 'CategoryX',
subcategory: 'Sub-CategoryX',
brand : 'BrandY',
type : 'TYPEX',
feature : 'FEATUREX'
]
]
]
}
I want to fill a text input in my form using an autocomplete widget that I have created using jquery ui. Everything works exactly how I want to, except when the form is submitted.
The problem is that when I submit the form, the text input is automatically reseted (I don't know why) and after that, the page reloads saying that the field is required (just validation working how it's supposed to). Of course, if it didn't reset the field everything would go fine.
I dont know if my select event of the autocomplete is working fine, here is the code:
select : function (e, ui) {
// I create a new attribute to store de database primary key of this option. This is
// usefull later on.
$('#%(input_id)s').attr('itemid', ui.item.real_value);
// I set the input text value's.
$('#%(input_id)s').val(ui.item.label);
}
Here is the full code of the autocomplete:
class AutocompleteTextInputWidget (forms.TextInput):
def media(self):
js = ("/js/autocomplete.js", "pepe.js")
def __init__(self, source, options={}, attrs={}):
self.options = None
self.attrs = {'autocomplete': 'off'}
self.source = source
self.minLength = 1
self.delay = 0
if len(options) > 0:
self.options = JSONEncoder().encode(options)
self.attrs.update(attrs)
def render(self, name, value=None, attrs=None):
final_attrs = self.build_attrs(attrs)
options = ''
if value:
final_attrs['value'] = escape(value)
if isinstance(self.source, list) or isinstance(self.source, tuple):
# Crea un Json con las opciones.
source = '['
for i in range(0, len(self.source)):
if i > 0:
source += ', '
source += '"' + self.source[i] + '"'
source += ']'
options = u'''
delay : %(delay)d,
minLength : %(minlength)s,
source : %(source)s
''' % {
'delay' : self.delay,
'minlength' : self.minLength,
'source' : source
}
elif isinstance(self.source, str):
options = u'''
delay : %(delay)d,
minLength : %(minlength)s,
source : function (request, response) {
if ($(this).data('xhr')) {
$(this).data('xhr').abort();
}
$(this).data('xhr', $.ajax({
url : "%(source_url)s",
dataType : "json",
data : {term : request.term},
beforeSend : function(xhr, settings) {
$('#%(input_id)s').removeAttr('itemid');
},
success : function(data) {
if (data != 'CACHE_MISS') {
response($.map(data, function(item) {
return {
label : item[1],
value: item[1],
real_value : item[0]
};
}));
}
},
}))
},
select : function (e, ui) {
$('#%(input_id)s').attr('itemid', ui.item.real_value);
$('#%(input_id)s').val(ui.item.label);
}
''' % {
'delay' : self.delay,
'minlength' : self.delay,
'source_url' : self.source,
'input_id' : final_attrs['id'],
}
if not self.attrs.has_key('id'):
final_attrs['id'] = 'id_%s' % name
return mark_safe(u'''
<input type="text" %(attrs)s/>
<script type="text/javascript">
$("#%(input_id)s").autocomplete({
%(options)s
});
</script>
''' % {
'attrs' : flatatt(final_attrs),
'options' : options,
'input_id' : final_attrs['id']
})
Tip: If I write some text without selecting it from the autocomplete, it still fails.
Another tip: If I set the field as optional it arrives to the view empty.
What should I do to make this work when I submit the form??? I have spent hours trying to
make this work. How can I make the form to recognise that I have allready filled that field?
Here is the code of the form:
test = forms.CharField(label = "autotest", widget = AutocompleteTextInputWidget('/myjsonservice'))
This is the rendered html:
<input type="text" autocomplete="off" id="id_test"/>
<script type="text/javascript">
$("#id_test").autocomplete({
delay : 0,
minLength : 0,
source : function (request, response) {
if ($(this).data('xhr')) {
$(this).data('xhr').abort();
}
$(this).data('xhr', $.ajax({
url : "/myjsonservice",
dataType : "json",
data : {term : request.term},
beforeSend : function(xhr, settings) {
$('#id_test').removeAttr('itemid');
},
success : function(data) {
if (data != 'CACHE_MISS') {
response($.map(data, function(item) {
return {
label : item[1],
value: item[1],
real_value : item[0]
};
}));
}
},
}))
},
select : function (e, ui) {
$('#id_test').attr('itemid', ui.item.real_value);
$('#id_test').val(ui.item.label);
}
});
</script>
Finally found the answer, the problem was that the "name" attribute wasn't rendered. Hence, the field could't get to the view as part of the request.
The final code of the autocomplete widget ended up like this:
class AutocompleteTextInputWidget (forms.TextInput):
def media(self):
js = ("/js/autocomplete.js", "pepe.js")
def __init__(self, source, options={}, attrs={}):
self.options = None
self.attrs = {'autocomplete': 'off'}
self.source = source
self.minLength = 1
self.delay = 0
if len(options) > 0:
self.options = JSONEncoder().encode(options)
self.attrs.update(attrs)
def render(self, name, value=None, attrs=None):
final_attrs = self.build_attrs(attrs)
options = ''
if value:
final_attrs['value'] = escape(value)
if isinstance(self.source, list) or isinstance(self.source, tuple):
# Crea un Json con las opciones.
source = '['
for i in range(0, len(self.source)):
if i > 0:
source += ', '
source += '"' + self.source[i] + '"'
source += ']'
options = u'''
delay : %(delay)d,
minLength : %(minlength)s,
source : %(source)s
''' % {
'delay' : self.delay,
'minlength' : self.minLength,
'source' : source
}
elif isinstance(self.source, str):
options = u'''
delay : %(delay)d,
minLength : %(minlength)s,
source : function (request, response) {
if ($(this).data('xhr')) {
$(this).data('xhr').abort();
}
$(this).data('xhr', $.ajax({
url : "%(source_url)s",
dataType : "json",
data : {term : request.term},
beforeSend : function(xhr, settings) {
$('#%(input_id)s').removeAttr('itemid');
},
success : function(data) {
if (data != 'CACHE_MISS') {
response($.map(data, function(item) {
return {
label : item[1],
value: item[1],
real_value : item[0]
};
}));
}
},
}))
},
select : function (e, ui) {
$('#%(input_id)s').attr('itemid', ui.item.real_value);
$('#%(input_id)s').val(ui.item.label);
}
''' % {
'delay' : self.delay,
'minlength' : self.delay,
'source_url' : self.source,
'input_id' : final_attrs['id'],
}
if not self.attrs.has_key('id'):
final_attrs['id'] = 'id_%s' % name
return mark_safe(u'''
<input type="text" name="%(name)s" %(attrs)s/>
<script type="text/javascript">
$("#%(input_id)s").autocomplete({
%(options)s
});
</script>
''' % {
'attrs' : flatatt(final_attrs),
'options' : options,
'input_id' : final_attrs['id'],
'name' : name
})
If someone knows how to improve this messy code it would be nice.
If someone knows about a nice widget documentation for django 1.4 (Other than the oficial, which sucks by the way) it would be nice too.
Bye, good coding everyone!!!