Referenced Before Assignment Django Des

Dentro do Python, como linguagem de programação, temos diferentes tipos de variáveis. Neste artigo vamos dar uma breve explicação de cada uma delas e mostrar como utilizar uma variável global dentro de uma função.

Definição de variável global no Python

Uma variável global é aquela que não foi definida dentro do escopo da função que a está utilizando. Como, por exemplo:

Definição de variável local no Python

Quando você define o valor da variável dentro da função, ela é uma variável local. Um exemplo de variável local é mostrado abaixo:


Esta variável local só pode ser utilizada depois que foi definida dentro da função. Caso tente utilizá-la antes de ser declarada dentro do código, uma mensagem de erro acontecerá:

Utilizando uma variável global dentro de uma função:

Para se utilizar uma variável global dentro de uma função, é necessário declará-la como global dentro dessa função.


Este código acima permitiria você utilizar o valor da global dentro da função, sem efetivamente ter que declarar este valor dentro da função. Uma diferença entre a variável global e a local no Python  é que a variável global pode ser declarada depois da função que a utilizará, sem que ocorra um erro semelhante ao descrito acima. Isto pode ser visto no exemplo abaixo:


Você pode declarar um valor para a variável global dentro da função. Isto faz com que o valor desta variável assuma o valor escrito dentro da função, deste ponto em diante dentro do código.


Atenção: Muito cuidado ao realizar esta operação, pois o controle das alterações de valor fica muito complexo.

Caso você conheça mais alguma forma de utilizar uma variável global dentro de uma função, compartilhe na área de comentários abaixo.

Agora que você descobriu a resposta para esta pergunta, e quiser explorar outras questões, pode conferir nossos vídeos sobre Python. Abaixo estão alguns exemplos:

Você também pode se inscrever em alguns canais que fazem broadcast em Python, como os a seguir:

emilyanndd

 

thenewboston

 

 

Outra maneira interessante de descobrir mais coisas interessantes sobre Python é acessar nossa página de projetos

variable_global=0

x=42

v=1

deff():

  v=10

printv

deff():

  printv

v=10

UnboundLocalError:localvariable'v'referencedbeforeassignment.

v=1

deff():

  globalv

printv

deff():

  printx

x=42

‘42’

x=3

def func():

globalx

  x=1

printx

‘1’

When planning your subclass, first give some thought to which existing class your new field is most similar to. Can you subclass an existing Django field and save yourself some work? If not, you should subclass the class, from which everything is descended.

Initializing your new field is a matter of separating out any arguments that are specific to your case from the common arguments and passing the latter to the method of (or your parent class).

In our example, we’ll call our field . (It’s a good idea to call your subclass , so it’s easily identifiable as a subclass.) It doesn’t behave like any existing field, so we’ll subclass directly from :

Our accepts most of the standard field options (see the list below), but we ensure it has a fixed length, since it only needs to hold 52 card values plus their suits; 104 characters in total.

All of the options without an explanation in the above list have the same meaning they do for normal Django fields. See the field documentation for examples and details.

Field deconstruction¶

The counterpoint to writing your method is writing the method. This method tells Django how to take an instance of your new field and reduce it to a serialized form - in particular, what arguments to pass to to re-create it.

If you haven’t added any extra options on top of the field you inherited from, then there’s no need to write a new method. If, however, you’re changing the arguments passed in (like we are in ), you’ll need to supplement the values being passed.

The contract of is simple; it returns a tuple of four items: the field’s attribute name, the full import path of the field class, the positional arguments (as a list), and the keyword arguments (as a dict). Note this is different from the method for custom classes which returns a tuple of three things.

As a custom field author, you don’t need to care about the first two values; the base class has all the code to work out the field’s attribute name and import path. You do, however, have to care about the positional and keyword arguments, as these are likely the things you are changing.

For example, in our class we’re always forcibly setting max_length in . The method on the base class will see this and try to return it in the keyword arguments; thus, we can drop it from the keyword arguments for readability:

If you add a new keyword argument, you need to write code to put its value into yourself:

More complex examples are beyond the scope of this document, but remember - for any configuration of your Field instance, must return arguments that you can pass to to reconstruct that state.

Pay extra attention if you set new default values for arguments in the superclass; you want to make sure they’re always included, rather than disappearing if they take on the old default value.

In addition, try to avoid returning values as positional arguments; where possible, return values as keyword arguments for maximum future compatibility. Of course, if you change the names of things more often than their position in the constructor’s argument list, you might prefer positional, but bear in mind that people will be reconstructing your field from the serialized version for quite a while (possibly years), depending how long your migrations live for.

You can see the results of deconstruction by looking in migrations that include the field, and you can test deconstruction in unit tests by just deconstructing and reconstructing the field:

fromdjango.dbimportmodelsclassHandField(models.Field):def__init__(self,*args,**kwargs):kwargs['max_length']=104super().__init__(*args,**kwargs)defdeconstruct(self):name,path,args,kwargs=super().deconstruct()delkwargs["max_length"]returnname,path,args,kwargs
fromdjango.dbimportmodelsclassCommaSepField(models.Field):"Implements comma-separated storage of lists"def__init__(self,separator=",",*args,**kwargs):self.separator=separatorsuper().__init__(*args,**kwargs)defdeconstruct(self):name,path,args,kwargs=super().deconstruct()# Only include kwarg if it's not the defaultifself.separator!=",":kwargs['separator']=self.separatorreturnname,path,args,kwargs
name,path,args,kwargs=my_field_instance.deconstruct()new_instance=MyField(*args,**kwargs)self.assertEqual(my_field_instance.some_attribute,new_instance.some_attribute)

Changing a custom field’s base class¶

You can’t change the base class of a custom field because Django won’t detect the change and make a migration for it. For example, if you start with:

and then decide that you want to use instead, you can’t change the subclass like this:

Instead, you must create a new custom field class and update your models to reference it:

As discussed in removing fields, you must retain the original class as long as you have migrations that reference it.

classCustomCharField(models.CharField):...
classCustomCharField(models.TextField):...
classCustomCharField(models.CharField):...classCustomTextField(models.TextField):...

Documenting your custom field¶

As always, you should document your field type, so users will know what it is. In addition to providing a docstring for it, which is useful for developers, you can also allow users of the admin app to see a short description of the field type via the django.contrib.admindocs application. To do this simply provide descriptive text in a class attribute of your custom field. In the above example, the description displayed by the application for a will be ‘A hand of cards (bridge style)’.

In the display, the field description is interpolated with which allows the description to incorporate arguments of the field. For example, the description for is:

description=_("String (up to %(max_length)s)")

Useful methods¶

Once you’ve created your subclass, you might consider overriding a few standard methods, depending on your field’s behavior. The list of methods below is in approximately decreasing order of importance, so start from the top.

Custom database types¶

Say you’ve created a PostgreSQL custom type called . You can subclass and implement the method, like so:

Once you have , you can use it in any model, just like any other type:

If you aim to build a database-agnostic application, you should account for differences in database column types. For example, the date/time column type in PostgreSQL is called , while the same column in MySQL is called . The simplest way to handle this in a method is to check the attribute.

For example:

The and methods are called by Django when the framework constructs the statements for your application – that is, when you first create your tables. The methods are also called when constructing a clause that includes the model field – that is, when you retrieve data using QuerySet methods like , , and and have the model field as an argument. They are not called at any other time, so it can afford to execute slightly complex code, such as the check in the above example.

Some database column types accept parameters, such as , where the parameter represents the maximum column length. In cases like these, it’s more flexible if the parameter is specified in the model rather than being hard-coded in the method. For example, it wouldn’t make much sense to have a , shown here:

The better way of doing this would be to make the parameter specifiable at run time – i.e., when the class is instantiated. To do that, just implement , like so:

Finally, if your column requires truly complex SQL setup, return from . This will cause Django’s SQL creation code to skip over this field. You are then responsible for creating the column in the right table in some other way, of course, but this gives you a way to tell Django to get out of the way.

The method is called by fields such as and that point to another field to determine their database column data types. For example, if you have an , you also need the foreign keys that point to that field to use the same data type:

fromdjango.dbimportmodelsclassMytypeField(models.Field):defdb_type(self,connection):return'mytype'
classPerson(models.Model):name=models.CharField(max_length=80)something_else=MytypeField()
classMyDateField(models.Field):defdb_type(self,connection):ifconnection.settings_dict['ENGINE']=='django.db.backends.mysql':return'datetime'else:return'timestamp'
# This is a silly example of hard-coded parameters.classCharMaxlength25Field(models.Field):defdb_type(self,connection):return'char(25)'# In the model:classMyModel(models.Model):# ...my_field=CharMaxlength25Field()
# This is a much more flexible example.classBetterCharField(models.Field):def__init__(self,max_length,*args,**kwargs):self.max_length=max_lengthsuper().__init__(*args,**kwargs)defdb_type(self,connection):return'char(%s)'%self.max_length# In the model:classMyModel(models.Model):# ...my_field=BetterCharField(25)
# MySQL unsigned integer (range 0 to 4294967295).classUnsignedAutoField(models.AutoField):defdb_type(self,connection):return'integer UNSIGNED AUTO_INCREMENT'defrel_db_type(self,connection):return'integer UNSIGNED'

Converting values to Python objects¶

If your custom class deals with data structures that are more complex than strings, dates, integers, or floats, then you may need to override and .

If present for the field subclass, will be called in all circumstances when the data is loaded from the database, including in aggregates and calls.

is called by deserialization and during the method used from forms.

As a general rule, should deal gracefully with any of the following arguments:

  • An instance of the correct type (e.g., in our ongoing example).
  • A string
  • (if the field allows )

In our class, we’re storing the data as a VARCHAR field in the database, so we need to be able to process strings and in the . In , we need to also handle instances:

Notice that we always return a instance from these methods. That’s the Python object type we want to store in the model’s attribute.

For , if anything goes wrong during value conversion, you should raise a exception.

importrefromdjango.core.exceptionsimportValidationErrorfromdjango.dbimportmodelsfromdjango.utils.translationimportgettext_lazyas_defparse_hand(hand_string):"""Takes a string of cards and splits into a full hand."""p1=re.compile('.{26}')p2=re.compile('..')args=[p2.findall(x)forxinp1.findall(hand_string)]iflen(args)!=4:raiseValidationError(_("Invalid input for a Hand instance"))returnHand(*args)classHandField(models.Field):# ...deffrom_db_value(self,value,expression,connection):ifvalueisNone:returnvaluereturnparse_hand(value)defto_python(self,value):ifisinstance(value,Hand):returnvalueifvalueisNone:returnvaluereturnparse_hand(value)

Converting Python objects to query values¶

Since using a database requires conversion in both ways, if you override you also have to override to convert Python objects back to query values.

For example:

Warning

If your custom field uses the , or types for MySQL, you must make sure that always returns a string type. MySQL performs flexible and unexpected matching when a query is performed on these types and the provided value is an integer, which can cause queries to include unexpected objects in their results. This problem cannot occur if you always return a string type from .

classHandField(models.Field):# ...defget_prep_value(self,value):return''.join([''.join(l)forlin(value.north,value.east,value.south,value.west)])

Converting query values to database values¶

Some data types (for example, dates) need to be in a specific format before they can be used by a database backend. is the method where those conversions should be made. The specific connection that will be used for the query is passed as the parameter. This allows you to use backend-specific conversion logic if it is required.

For example, Django uses the following method for its :

In case your custom field needs a special conversion when being saved that is not the same as the conversion used for normal query parameters, you can override .

defget_db_prep_value(self,value,connection,prepared=False):value=super().get_db_prep_value(value,connection,prepared)ifvalueisnotNone:returnconnection.Database.Binary(value)returnvalue

Preprocessing values before saving¶

If you want to preprocess the value just before saving, you can use . For example, Django’s uses this method to set the attribute correctly in the case of or .

If you do override this method, you must return the value of the attribute at the end. You should also update the model’s attribute if you make any changes to the value so that code holding references to the model will always see the correct value.

Specifying the form field for a model field¶

To customize the form field used by , you can override .

The form field class can be specified via the and arguments; the latter is used if the field has choices specified, the former otherwise. If these arguments are not provided, or will be used.

All of the dictionary is passed directly to the form field’s method. Normally, all you need to do is set up a good default for the (and maybe ) argument and then delegate further handling to the parent class. This might require you to write a custom form field (and even a form widget). See the forms documentation for information about this.

Continuing our ongoing example, we can write the method as:

This assumes we’ve imported a field class (which has its own default widget). This document doesn’t cover the details of writing custom form fields.

classHandField(models.Field):# ...defformfield(self,**kwargs):# This is a fairly standard way to set up some defaults# while letting the caller override them.defaults={'form_class':MyFormField}defaults.update(kwargs)returnsuper().formfield(**defaults)

Emulating built-in field types¶

If you have created a method, you don’t need to worry about – it won’t be used much. Sometimes, though, your database storage is similar in type to some other field, so you can use that other field’s logic to create the right column.

For example:

No matter which database backend we are using, this will mean that and other SQL commands create the right column type for storing a string.

If returns a string that is not known to Django for the database backend you are using – that is, it doesn’t appear in – the string will still be used by the serializer, but the default method will return . See the documentation of for reasons why this might be useful. Putting a descriptive string in as the type of the field for the serializer is a useful idea if you’re ever going to be using the serializer output in some other place, outside of Django.

classHandField(models.Field):# ...defget_internal_type(self):return'CharField'

Converting field data for serialization¶

To customize how the values are serialized by a serializer, you can override . Using is the best way to get the field’s value prior to serialization. For example, since our uses strings for its data storage anyway, we can reuse some existing conversion code:

classHandField(models.Field):# ...defvalue_to_string(self,obj):value=self.value_from_object(obj)returnself.get_prep_value(value)

Some general advice¶

Writing a custom field can be a tricky process, particularly if you’re doing complex conversions between your Python types and your database and serialization formats. Here are a couple of tips to make things go more smoothly:

  1. Look at the existing Django fields (in ) for inspiration. Try to find a field that’s similar to what you want and extend it a little bit, instead of creating an entirely new field from scratch.
  2. Put a method on the class you’re wrapping up as a field. There are a lot of places where the default behavior of the field code is to call on the value. (In our examples in this document, would be a instance, not a ). So if your method automatically converts to the string form of your Python object, you can save yourself a lot of work.

0 thoughts on “Referenced Before Assignment Django Des”

    -->

Leave a Comment

Your email address will not be published. Required fields are marked *