over 1 year ago
function test() {
  console.log(this);    // window

  function inner(){
    console.log(this);  // window

  }
  inner();
}

The this keyword inside the inner() function also refers to the Window object. This is because:

When a function is invoked as a function rather than as a method, the this keyword refers to the global object. Confusingly, this is true even when a nested function is invoked (as a function) within a containing method that was invoked as a method: the this keyword has one value in the containing function but (counterintuitively) refers to the global object within the body of the nested function.

 
over 1 year ago

To understand what configurable does, I did a little experiement:

var obj = {};
obj.name = 'cheng';
console.log(obj.name); // cheng


Object.defineProperty(obj, 'name', {configurable: false});
var des = Object.getOwnPropertyDescriptor(obj, 'name');
console.log('Writable: ' + des.writable);          // true

console.log('Configurable: ' + des.configurable);  // false

console.log('Enumerable: ' + des.enumerable);      // true


delete obj.name;       // the delete operation returns false

console.log(obj.name); // prints cheng

Object.defineProperty(obj, 'name', {writable: false});
des = Object.getOwnPropertyDescriptor(obj, 'name');
console.log('Writable: ' + des.writable); // false, so it is ok to change writable to false

Object.defineProperty(obj, 'name', {enumerable: false}); // "TypeError: Cannot redefine property: name, which means enumerable cannot be changed if configurable is set to false

I wonder if I can change those properties to true if they started as false:

var obj = {};

Object.defineProperty(obj, 'name', {configurable: false, writable:false, enumerable: false, value:'cheng'});
var des = Object.getOwnPropertyDescriptor(obj, 'name');
console.log('Writable: ' + des.writable);          // false

console.log('Configurable: ' + des.configurable);  // false

console.log('Enumerable: ' + des.enumerable);      // false


Object.defineProperty(obj, 'name', {configurable: true});   // "TypeError: Cannot redefine property: name

Object.defineProperty(obj, 'name', {writable: true});   // "TypeError: Cannot redefine property: name

Object.defineProperty(obj, 'name', {enumerable: true}); // "TypeError: Cannot redefine property: name

Conclusion

When configurable is set fo false:

  • you cannot delete the property by using the keyword delete
  • you cannot change the property enumerable
  • you CAN change the property writable from true to false but not the other way around
  • you cannot change configurable to true if its current state is false
 
over 1 year ago

I did a little experiment just want to see what's the difference between defining object properties "normally" vs defining them using defineProperty:

var obj = {};
Object.defineProperty(obj, 'name', {value: 'cheng'} );
console.log(obj.name);

var des = Object.getOwnPropertyDescriptor(obj, 'name');
console.log('Writable: ' + des.writable);
console.log('Configurable: ' + des.configurable);
console.log('Enumerable: ' + des.enumerable);
console.log('getter: ' + des.getter);
console.log('setter: ' + des.setter);
obj.name = 'test';
console.log(obj.name);

var obj2 = { name: 'cheng'};
var des2 = Object.getOwnPropertyDescriptor(obj2, 'name');
console.log('Writable: ' + des2.writable);
console.log('Configurable: ' + des2.configurable);
console.log('Enumerable: ' + des2.enumerable);
console.log('getter: ' + des2.getter);
console.log('setter: ' + des2.setter);
obj2.name = 'test';
console.log(obj2.name);

Here is the output:

"cheng"
"Writable: false"
"Configurable: false"
"Enumerable: false"
"getter: undefined"
"setter: undefined"
"cheng"
"Writable: true"
"Configurable: true"
"Enumerable: true"
"getter: undefined"
"setter: undefined"
"test"

Conclusion

When using defineProperty, you have to define all of the properties by yourself, otherwise they are default to false. But if the properties are define normally, then configurable, enumerable and writable are all default to true.

 
over 1 year ago

I had a local postfix mail server setup and trying to send an email with the following setting:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = False
EMAIL_HOST = 'localhost'
EMAIL_PORT = 25
DEFAULT_FROM_EMAIL = 'TEST <test@blah.com>'

Here is the code responsible for sending the email:

email = EmailMessage(
        subject=subject,
        body=message,
        from_email='test@blah.com',
        to=uers,
        cc=applicants + cc_email,
        reply_to=uers,
    )
    email.content_subtype = 'html'

    try:
        email.send(fail_silently=False)

Because the mail server is setup locally, there is no need to supply any user name or password. So I didn't bother to specify them in the settings.py. When I had the server running, here is the error that I encountered:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/cheng/.virtualenvs/diandi/lib/python2.7/site-packages/django/core/mail/__init__.py", line 61, in send_mail
    return mail.send()
  File "/home/cheng/.virtualenvs/diandi/lib/python2.7/site-packages/django/core/mail/message.py", line 292, in send
    return self.get_connection(fail_silently).send_messages([self])
  File "/home/cheng/.virtualenvs/diandi/lib/python2.7/site-packages/django/core/mail/backends/smtp.py", line 100, in send_messages
    new_conn_created = self.open()
  File "/home/cheng/.virtualenvs/diandi/lib/python2.7/site-packages/django/core/mail/backends/smtp.py", line 67, in open
    self.connection.login(self.username, self.password)
  File "/usr/local/lib/python2.7.10/lib/python2.7/smtplib.py", line 585, in login
    raise SMTPException("SMTP AUTH extension not supported by server.")
SMTPException: SMTP AUTH extension not supported by server.

The error message is very misleading, but this particular caught my eyes:

self.connection.login(self.username, self.password)

I didn't specify any username and password because I don't think I need them. Maybe I should put them back in? So I added the following lines:

EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''

Wholah, it worked! But why? So I looked under the hood:

self.username = settings.EMAIL_HOST_USER if username is None else username
self.password = settings.EMAIL_HOST_PASSWORD if password is None else password

When using the EmailBackend class, a username and password can be passed in as parameters. I didn't pass either of them in my view function. Thus, Django defaults them to settings.EMAIL_HOST_USER and settings.EMAIL_HOST_PASSWORD. However, EMAIL_HOST_USER and EMAIL_HOST_PASSWORD are not defined in the settings file. So by the time self.connection.login(self.username, self.password) is called, both username and password are None.

The error message SMTP AUTH extension not supported by server is generated by python's own smtp library. I guess Django can throw a more meaningful error before this to make this error more apparent.

 
over 1 year ago

When django template renders parameters passed from views, it auto escapes characters that are considered as HTML entities.In English, that means characters like the double quote is convereted to &quot;. This default behavior may not be what you want so it is important to know how to turn it off.

Here is a situation that I encountered while working on a project. I need to pull all of the staffusers out of the database and then put their email addresses in the following format:

[{'email': 'user1@blah.com'}, {'email': 'user2@blah.com'}, ...]

In the view function, I pull the email addresses out of the database and converted them into JSON format:

def a_view_handler(request):
  contacts = User.objects.get(is_staff=True)
  email_list = []
  for c in contacts:
      if c.email:
          email_list.append({'email': c.email})
   
  return render(request, 'contacts.html', {'email_list': json.dumps(email_list)}

Then, in the template:

emails = {{ email_list }}

When I tested the page, this is what I see:

emails = [{&quot;email&quot;: &quot;test@test.com&quot;}, ...]

This happened because when python converts the email_list to json, it added double quotes around the key and value pairs. When Django template renders it, it auto escaped the double quote character (meaning convert it to #quot;). To stop Django from doing that:

{% autoescape off %}
    emails = {{ email_list }}
{% endautoescape %}

The autoescape block controls whether the content inside of it should be auto escaped or not. So use it when you dont want to see those HTML entities.

 
over 1 year ago

The common command line arguments that I use with the netstat command:

-t: display tcp connections
-u: display udp connections
-l: display ports that are in LISTEN state
-p: display process id
-n: display ip addresses instead of host names

To see if a port has been occupied:

> netstat -tulpn | grep :port_number
 
over 1 year ago

I was trying to start a Postfix mail server on a Ubuntu 12.4:

> service postfix start
* Starting Postfix Mail Transport Agent postfix                                     [ OK ]

> telnet localhost 25
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused

What? I just started the service and it should be running on port 25 but telnet won't connect, so I had to dig more:

> netstat -tulpn | grep :25 | LISTEN

Nothing?! After some Google search, I located the log file at /var/log/mail.err:

> less /var/log/mail.err

fatal: open lock file /var/lib/postfix/master.lock: cannot open file: Permission denied

No Permission? Ok, let's give it the permission:

> chown postfix.postfix -R /var/lib/postfix
> service postfix start
 * Starting Postfix Mail Transport Agent postfix
   ...done.

> netstat -tulpn | grep :25 | grep LISTEN
tcp        0      0 0.0.0.0:25              0.0.0.0:*               LISTEN      22166/master

Great, it worked!

P.S. chown postfix.postfix = chown postfix:postfix = chown owner_name:group_name

 
over 1 year ago

I have a site which sends out emails through my corporate email account (Outlook), here are the steps need to send emails using Django:

Let's say my username is cheng@blah.com and the password is 123 for my email account.

  1. Make sure the following settings are in your settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
EMAIL_HOST = 'email.blah.com' # double check the settings in your outlook mailbox to make sure the host name is correct

EMAIL_PORT = 587  # double check the settings in your outlook mailbox and make sure the port number is correct

EMAIL_HOST_USER = 'cheng'  # don't include the @blah.com part! I have made this stupid mistakes before

EMAIL_HOST_PASSWORD = '123'
  1. In your code:
from django.core.mail import EmailMessage
import traceback

def send_email():
    email = EmailMessage(
        subject='subject',
        body='message',
        from_email='cheng@blah.com',
        to=['someone1@blah.com', 'someone2@blah.com'],
        cc=['someone3@blah.com'],
        reply_to=['cheng@blah.com'],  # when the reply or reply all button is clicked, this is the reply to address, normally you don't have to set this if you want the receivers to reply to the from_email address

    )
    email.content_subtype = 'html' # if the email body contains html tags, set this. Otherwise, omit it

     try:
        email.send(fail_silently=False)
        return HttpResponseRedirect(reverse('apply:success'))
    except Exception:
        print traceback.format_exc() # you probably want to add a log here instead of console printout

That should be it :)

 
over 1 year ago

Two special characters are involved here:

  • Carriage Return (CR or \r in many programming languages)
  • Line Feed (LR or \n)

I never understood what those terms mean until I read that they are inherited from typewriters. When typing on a type writer, two physical actions have to happen in order to continue typing on the next line. First, the carriage needs to return to the very beginning (the far left and makes the ding sound). Then, the typewriter needs to move the page up a little so that we can type on a new line. The moving page up action is called line feed.

Windows uses CR+LR (i.e. \r\n) to denote a new line but on Unix and Unix-like machines (e.g. Linux), only LF or \n is used.

Some interesting behaviour of how CRLF is treated in JavaScript [*]:

JavaScript and XPath treat CRLF pairs as two line breaks. ^ matches in the middle of and after CRLF, while $ matches before and in the middle of CRLF.

 
over 1 year ago

I run into a problem today, here is the snippet:

var i = 101;
console.log('101: ' +  i.toString(2));
console.log('101 >> 1: ' + (i >> 1).toString(2));

var l = -101;
console.log('-101: ' + l.toString(2));
console.log('-101 >> 1: ' + (l >> 1).toString(2));'

Output:

"101: 1100101"
"101 >> 1: 110010"
"-101: -1100101"
"-101 >> 1: -110011"

Why -101 >> 1 is -110011 instead of -110010?

Here is what I learned by reading Professional JavaScript for Web Developers:

When js stores a negative number, it does the following things:

  1. get the binary representation of the absolute value of the negative number
  2. replace 0s with 1s and 1s with 0s
  3. add 1 to the result of step 2

So in my case -101 >> 1, we first convert -101 to its binary representation:

The binary representation of Math.abs(-101) is:

0000 0000 0000 0000 0000 0000 0110 0101

invert the 0s and 1s:

1111 1111 1111 1111 1111 1111 1001 1010

add 1 to the end:

1111 1111 1111 1111 1111 1111 1001 1011

Now, shift it to the right by 1:

1111 1111 1111 1111 1111 1111 1100 1101

The binary above should be the correct result of -101 >> 1, but when logging a negative number's binary representation, Javascript simply puts a negative sign in front of the binary representation of the positive number:

var x = 15;
console.log(x.toString(2)); // output: 1111


var y = -15;
console.log(y.toString(2)); // output: -1111

For our example, this means that when logging the result of -101 >> 1, JS will output minus sign + the binary representation of the positive number. But the positive number is not 101 >> 1 because 101 >> 1 gives you:

(101 >> 1).toString(2);  // output: 110010

(-101 >> 1).toString(2); // output: -110011, not -110010!

To get the correct result, we have to reverse the aforementioned step 1-3:

1111 1111 1111 1111 1111 1111 1100 1101   // this is the result we get from step 4

Reverse step 3 by subtracting 1, we get:

1111 1111 1111 1111 1111 1111 1100 1100

Reverse step 2 by invert 0s and 1s:

0000 0000 0000 0000 0000 0000 0011 0011

Reverse step 1 by converting this binary to integer:

parseInt(110011, 2); // output: 51

Finally, when JS logs the result of -101 >> 1, it should be in the format of minus sign + the binary representation of 51:

(51).toString(2);        // output:  110011

(-101 >> 1).toString(2); // output: -110011