Errors in rendering booklist to a template - django

from django.shortcuts import render
from django.http import HttpResponse
booksList = [
{ 'id' = '1',
'title' = "Beginner's Course in Django",
'description' = 'Foundational Course in Django'}
{ 'id' = '2',
'title' = "Intermediate Course in Django",
'description' = 'Next steps in Django'
},
{
'id' = '3',
'title' = "Advanced Course in Django",
'description' = 'The complexities of Django'
},
]
I am rendering data to a template using the above bookList and getting two errors:
'[' was not closed Pylance and
'{' was not closed Pylance
Kindly advise.

I have just found the error: colon (:) should be used in place of the equal sign (=).The following code works:
from django.shortcuts import render
from django.http import HttpResponse
booksList = [
{ 'id' : '1',
'title' : "Beginner's Course in Django",
'description' : 'Foundational Course in Django'},
{ 'id' : '2',
'title' : "Intermediate Course in Django",
'description' : 'Next steps in Django'
},
{
'id' : '3',
'title' : "Advanced Course in Django",
'description' : 'The complexities of Django'
},
]

Related

How do you setup custom fields in a unit test on Drupal 8

I am trying to test the functionality of methods related to custom fields on the user entity.
When I try to set the value of these fields in the setup method of my unit test, I get the error: Field * is unknown.
My initial test looks like this:
namespace Drupal\my_module\Entity;
use Drupal\Tests\BrowserTestBase;
class UserTest extends BrowserTestBase
{
/** #var \Drupal\my_module\Entity\User|false */
protected $user;
protected static $modules = ['field', 'user', 'commerce_payment', 'my_module'];
protected $strictConfigSchema = FALSE;
public function setUp() {
parent::setUp();
$this->user = $this->drupalCreateUser();
$this->user->addRole('member');
$this->user->set('field_user_abc', [['value' => '123']]);
}
public function testInstance1() {
$this->assertNotEmpty($this->user->get('field_user_abc')->get(0)->getValue());
}
}
It is failing at the ->set() call in the setup method.
I don't really understand why I would need to recreate the field if Drupal is bootstrapped with my project database. However, I read in some related posts that that might be the case. Following the example in /core/modules/user/tests/src/Functional/UserCreateTest.php, I tried the following, but the result was the same...
namespace Drupal\my_module\Entity;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\BrowserTestBase;
class UserTest extends BrowserTestBase
{
protected $user;
protected static $modules = ['field', 'user', 'commerce_payment', 'my_module'];
protected $strictConfigSchema = FALSE;
public function setUp() {
parent::setUp();
$this->user = $this->drupalCreateUser();
$this->user->addRole('member');
$this->drupalLogin($this->user);
// Create a field.
$field_name = 'field_user_abc';
FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'user',
'module' => 'core',
'type' => 'string',
'cardinality' => 1,
'locked' => FALSE,
'indexes' => [],
'settings' => [
'max_length' => 14,
'is_ascii' => false,
'case_sensitive' => false,
],
])->save();
FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'user',
'label' => 'ABC',
'bundle' => 'user',
'description' => '',
'required' => FALSE,
'settings' => [],
])->save();
$this->user->set($field_name, [['value' => '123']]);
}
public function testInstance1() {
$this->assertNotEmpty($this->user->get('field_user_abc')->get(0)->getValue());
}
}
It's because your storage type is set to string instead of text.
You should also remove the 'module' => 'core' and add text in your modules dependencies.
protected static $modules = ['field', 'text', 'user', 'commerce_payment', 'my_module'];
FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'user',
'type' => 'text',
'cardinality' => 1,
'locked' => FALSE,
'indexes' => [],
'settings' => [
'max_length' => 14,
'is_ascii' => false,
'case_sensitive' => false,
],
])->save();
This will result in a varchar(14) in database.
Once you export the config, the field.storage.user.field_user_abc.yml will look like this :
uuid: 2656c022-1ff2-4868-b07d-c26ff3531aac
langcode: fr
status: true
dependencies:
module:
- text
- user
id: user.field_user_abc
field_name: field_user_abc
entity_type: user
type: text
settings:
max_length: 14
is_ascii: false
case_sensitive: false
module: text
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false

Drupal - Ajax validation only works when I am connected

I am new in Drupal world and I'm trying to use the Drupal From API to create a contact form with Ajax validation.
I'm facing 2 issues:
My following form (see below) works well but only when the user is connected as administrator. When I am not logged in as an administrator, it does not work.
I also created a custom Block to display my Form, unfortunately the block does not appear when I am logged in.
I try to follow this guide whitout success: https://panshul1410.blog/2018/07/15/drupal-8-ajax-validations-for-custom-form/
Here is the form I created:
<?php
namespace Drupal\dalcom_contact\Form;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class DalcomContactForm extends FormBase {
/**
* {#inheritdoc}
*/
public function getFormId() {
return 'dlcm_cform';
}
public function buildForm(array $form, FormStateInterface $form_state, $params = NULL) {
// Disable caching.
$form['#cache']['max-age'] = 0;
// Disable browser HTML5 validation.
$form['#attributes']['novalidate'] = 'novalidate';
// This will generate an anchor scroll to the form when submitting.
$form['#action'] = '#dlcm_cform';
$form['mail_visitor'] = [
'#type' => 'email',
'#placeholder' => t('E-mail*'),
'#description' => 'Your mail',
'#required' => TRUE,
'#ajax' => [
'callback' => 'Drupal\dalcom_contact\Form\DalcomContactForm::mailValidateCallback',
'effect' => 'fade',
'event' => 'change',
'progress' => [
'type' => 'throbber',
'message' => NULL,
],
],
];
$form['message_visitor'] = [
'#type' => 'textarea',
'#placeholder' => t('Message*'),
'#description' => 'Your message',
'#required' => TRUE,
'#ajax' => [
'callback' => 'Drupal\dalcom_contact\Form\DalcomContactForm::messValidateCallback',
'effect' => 'fade',
'event' => 'change',
'progress' => [
'type' => 'throbber',
'message' => NULL,
],
],
];
$form['accept_box'] = [
'#type' => 'checkbox',
'#title' => $this
->t('I accept the CME terms of use'),
'#required' => TRUE,
'#ajax' => [
'callback' => 'Drupal\dalcom_contact\Form\DalcomContactForm::acceptboxalidateCallback',
'effect' => 'fade',
'event' => 'change',
'progress' => [
'type' => 'throbber',
'message' => NULL,
],
],
];
$form['candidate_copy'] = [
'#type' => 'checkbox',
'#title' => t('Send me a copy of my message.'),
];
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = [
'#type' => 'button',
'#value' => $this->t('Send'),
'#ajax' => [
'event' => 'click',
'progress' => [
'type' => 'throbber',
'message' => 'Sending...',
],
],
];
$form['#theme'] = 'dalcom_contact_theme';
return $form;
}
public function submitForm(array &$form, FormStateInterface $form_state) {
foreach ($form_state->getValues() as $key => $value) {
drupal_set_message($key . ': ' . $value);
}
}
public function mailValidateCallback(array &$form, FormStateInterface $form_state) {
$ajax_response = new AjaxResponse();
if (!$form_state->getValue('mail_visitor') || empty($form_state->getValue('mail_visitor'))) {
$text = 'No email registered';
$color = 'red';
}
elseif (!filter_var($form_state->getValue('mail_visitor'), FILTER_VALIDATE_EMAIL)) {
$text = 'Invalid email address';
$color = 'red';
}
else {
$text = 'Valid mail';
$color = 'green';
}
$ajax_response->addCommand(new HtmlCommand('#edit-mail-visitor--description', $text));
$ajax_response->addCommand(new InvokeCommand('#edit-mail-visitor--description', 'css', ['color', $color]));
return $ajax_response;
}
public function messValidateCallback(array &$form, FormStateInterface $form_state) {
$ajax_response = new AjaxResponse();
if (!$form_state->getValue('message_visitor') || empty($form_state->getValue('message_visitor'))) {
$text = 'No messages written';
$color = 'red';
}
elseif (strlen($form_state->getValue('message_visitor')) < 6) {
$text = 'At least 7 characters';
$color = 'red';
}
else {
$text = 'Messages written';
$color = 'green';
}
$ajax_response->addCommand(new HtmlCommand('#edit-message-visitor--description', $text));
$ajax_response->addCommand(new InvokeCommand('#edit-message-visitor--description', 'css', ['color', $color]));
return $ajax_response;
}
public function acceptboxValidateCallback(array &$form, FormStateInterface $form_state) {
$ajax_response = new AjaxResponse();
if (empty($form_state->getValue('accept-box'))) {
$text = 'You must accept our termes of use to continue';
$color = 'red';
}
$ajax_response->addCommand(new HtmlCommand('#edit-accept-box--description', $text));
$ajax_response->addCommand(new InvokeCommand('#edit-accept-box--description', 'css', ['color', $color]));
return $ajax_response;
}
}
It works very well. But Ajax validation only works when I am connected. When I am not logged in as an administrator, it does not work.
Here is the block to display the form in my footer.
<?php
namespace Drupal\dalcom_contact_block\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormInterface;
/**
* Provides a 'dalcom contact' block.
*
* #Block(
* id = "dalcom_contact_block",
* admin_label = #Translation("Dalcom Contact Block"),
* category = #Translation("Block du formulaire Dalcom Contact")
* )
*/
class DalcomContactBlock extends BlockBase {
/**
* {#inheritdoc}
*/
public function build() {
$form = \Drupal::formBuilder()->getForm('\Drupal\dalcom_contact\Form\DalcomContactForm');
return $form;
}
}
As explain upper, the block does not appear when I am logged in. I have access to the form only through the path I defined in module.routing.yml. And when it appears (so when I'm not logged in), Ajax doesn't work on this block form either.
Does anyone have any idea what could cause this?
Please help me.
Edited:
Here is the module.routing.yml file
dalcom_contact.form:
path: '/contact-us.html'
defaults:
_title: 'Contact Us'
_form: 'Drupal\dalcom_contact\Form\DalcomContactForm'
requirements:
_permission: 'access content'
block.info.yml
name: Dalcom Contact Block
type: module
description: Block du formulaire Dalcom Contact.
core: 8.x
package: Custom
dependencies:
- node
- block
Updated:
I notice that when the form does not appear (when I am connected), it appears in its place
<span data-big-pipe-placeholder-id="…"></span>
I've done some research on this, but I can't get out of it. Maybe this will help you to know what's going on.
I finally found the solution on the Drupal forum. I put it here, maybe it will help in the future a novice who will be in my case.
What was missing was simply a list of essential javascripts for anonymous users. It seems that at one time it wasn't necessary, but now we have to add them otherwise they wouldn't be loaded for anonymous users.
So what I needed to do was add this to my theme.libraries.yml file
my_scripts:
version: VERSION
js:
js/scripts.js: {}
dependencies:
- core/jquery
- core/drupal.ajax
- core/drupal
- core/drupalSettings
- core/jquery.once

How to sort values in dictionaries

Below is the sample code snippet i want to sort
Variable books is a dictionaries contains values.
books = {
1234 : {
'isbn' : '1234',
'name' : 'Test Book 1',
'publish' : 'January 1990'
},
2345 : {
'isbn' : '2345',
'name' : 'Sample Book',
'publish' : 'December 2000'
}
}
for key, values in books.items():
values.sort(key=lambda x: int(x['name']))
When i compiled the code. I have an error encounntered. 'dict' object has no attribute 'sort'
How can I sort the books by values with the key 'name'
You need to create an OrderedDict from books, which is a dict that keeps track of the insertion order (like list). This module is sub class of dict, and has sorting functionality. You then can use
>>> OrderedDict(sorted(books.items(), key=lambda x: x[1]))
OrderedDict([(1234, {'isbn': '1234', 'name': 'Test Book 1', 'publish': 'January 1990'}), (2345, {'isbn': '2345', 'name': 'Sample Book', 'publish': 'December 2000'})])
If you don't need it to be a dict, you can use the sorted function and have list of tuples (with dicts inside the items)

How to customise the Regex validation messages in a Zend\Form keeping them reusable?

The default validation error message for Regex is
"The input does not match against pattern '%pattern%'"
I can replace it by a custom one like
"Please make an input according to the pattern '%pattern%'"
But then I still have a not really user-friendly message with the internal regex in it. I also can write
"Only capital letters are allowed"
But in this case I need to write a new message for every single regex field.
Is it possible / How to create own, but still flexible/reusable/parameterizable messages?
An example of what I want:
public function getInputFilterSpecification()
{
return [
'foo' => [
'validators' => [
[
'name' => 'Regex',
'options' => [
'pattern' => '/^[A-Z0-9:]*$/',
'pattern_user_friendly' => 'capital letters, numbers, and colons',
'message' => 'The input may only contain the following characters: %pattern_user_friendly%.'
]
],
]
],
'bar' => [
'validators' => [
[
'name' => 'Regex',
'options' => [
// new pattern
'pattern' => '/^[A-Z~:\\\\]*$/',
// new user friendly pattern description
'pattern_user_friendly' => 'capital letters, tildes, colons, and backslashes',
// still the same message
'message' => 'The input may only contain the following characters: %pattern_user_friendly%.'
]
],
]
],
];
}
The solution is to create a custom Validator (extending the Regex), to extend there the list of messageVariables, and to add the logic for setting the value as a property:
class Regex extends ZendRegex
{
protected $patternUserFriendly;
public function __construct($pattern)
{
// s. https://github.com/zendframework/zend-validator/blob/master/src/Regex.php#L34-L36
$this->messageVariables['patternUserFriendly'] = 'patternUserFriendly';
$this->messageTemplates[self::NOT_MATCH] =
'The input may only contain the following characters: %patternUserFriendly%.'
;
parent::__construct($pattern);
if (array_key_exists('patternUserFriendly', $pattern)) {
$this->patternUserFriendly = $pattern['patternUserFriendly'];
}
}
}
class MyFieldset extends ZendFieldset implements InputFilterProviderInterface
{
...
public function init()
{
parent::init();
$this->add(
[
'type' => 'text',
'name' => 'foo',
'options' => [
'label' => _('foo')
]
]);
$this->add(
[
'type' => 'text',
'name' => 'bar',
'options' => [
'label' => _('bar')
]
]);
}
public function getInputFilterSpecification()
{
return [
'bar' => [
'validators' => [
[
'name' => 'MyNamespace\Validator\Regex',
'options' => [
'pattern' => '/^[a-zA-z]*$/',
'patternUserFriendly' => '"a-z", "A-Z"'
]
]
]
]
];
}
}

Template Helper Functions

Is it possible to pass a variable into a template helper function to determine which object is returned, without creating a new template for each? Something like this, maybe?
{{#each frame}}
<p>{{name}}</p>
<p>{{description}}</p>
{{/each}}
Template.templateName.helpers({
classic : [
{
name : 'first option',
description : 'this is option 1'
},
{
name : 'second option',
description : 'this is option 2'
}],
versions : [
{
name : 'first option',
description : 'this is option 1'
},
{
name : 'second option',
description : 'this is option 2'
}],
{
'frame' : function(boardSpecs){
if (boardSpecs[0] == "classic"){
return classic;
}
else if (boardSpecs[0] == "versions"){
return versions;
}
}
})
You can probably play with {{#if}}.
For example only with classic and versions:
Template:
{{#each frame}}
{{#if classic.active}}
<p>{{classic.name}}</p>
<p>{{classic.description}}</p>
{{/if}}
{{#if versions.active}}
<p>{{versions.name}}</p>
<p>{{versions.description}}</p>
{{/if}}
{{/each}}
Helpers:
Template.templateName.helpers({
active: {
classic: function(){ return Session.get("active") == "classic"; }
versions: function(){ return Session.get("active") == "versions"; }
},
classic : [
{
name : 'first option',
description : 'this is option 1'
},
{
name : 'second option',
description : 'this is option 2'
}],
versions : [
{
name : 'first option',
description : 'this is option 1'
},
{
name : 'second option',
description : 'this is option 2'
}]
})
Then you can set Session.set("active", "name of the one you want") when you need to change view.