recently i have created a model for storing some states in my DB. It's very simple i only store id and name.
class PayType(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
class Meta:
ordering = ('-name',)
def __str__(self):
return "[{0}] {1}".format(self.id, self.name)
Here you can see a migration where i insert default values in the DB.
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('stockmanager', '0023_pagotipo_remove_carrito_unidades_totales_and_more'),
]
def insertPayType(apps, schema_editor):
PayType = apps.get_model('stockmanager', 'PayType')
PayType(name="Credit").save()
PayType(name="Debit").save()
PayType(name="Cash").save()
operations = [
migrations.RunPython(insertPayType)
]
As you can see, i'll have fixed rows in DB.
I'd like to add properties in the PayType Model, in order to have easy access the instances of the model. Like a static attribute in java.
class PayType(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
class Meta:
ordering = ('-name',)
def __str__(self):
return "[{0}] {1}".format(self.id, self.name)
# I'm looking for something like this
#property
def CREDIT(self):
return self.objects.get(id = 1)
# I also tried with this, same result :(
#property
def DEBIT(self):
return get_object_or_404(self, id = 2)
I tried to access in a diferent part of my code with this
class Pay(models.Model):
id = models.AutoField(primary_key=True)
order_id = models.IntegerField()
value = models.FloatField()
pay_type = models.ForeignKey(PayType, on_delete=models.CASCADE)
Pay(
order_id = order_id,
value = 100,
# I want this
pay_type = Pay.CASH #Error line
).save()
The thing is that i receive this error
Cannot assign "<property object at 0x00000138FEAC7F60>": "Pay.pay_type" must be a "PayType" instance.
Any idea?
Can't you just write your logic this way? It should work as indeed:
# I'm looking for something like this
#property
def CREDIT(self):
return PayType.objects.get(id = 1)
# I also tried with this, same result :(
#property
def DEBIT(self):
return get_object_or_404(PayType, id = 2)
You could also try to fetch PayType class using meta framework or try to catch it using type(self), I think it should work.
Related
I was trying to make it this way but instead of {{product.id}}, folder is named None.
I read some articles about that, and I found out that is because folder is being made before whole object. So how should I do that to make it work?
models.py:
def getImageURL(self, filename):
return "products_images/" + str(self.pk) + "/product_image.png"
class Product(models.Model):
name = models.CharField(max_length=200)
image = models.ImageField(upload_to=getImageURL, default="media/static/products_images/default.png" )
changed my funtion to:
def getImageURL(instance, filename):
return "products_images/{}/product_image.png".format(instance.id)
But it works only while edditing existing object. When I'm trying to make a new one id is set to None.
Edit:
ok, finally I did it this way:
def getFileNumber():
queryset = Product.objects.all().order_by('pk')
last = queryset.last()
last_id = last.id
file_number = last_id+1
return str(file_number)
def getImageURL(instance, filename):
return "products_images/{}/product_image.png".format(getFileNumber())
class Product(models.Model):
name = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
availability = models.IntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2)
Probably it is not the best way to do that but it's very simple to understand so why not to use it.
Some debugging:
def getFileNumber():
queryset = Product.objects.all().order_by('pk')
if queryset:
last = queryset.last()
last_id = last.id
file_number = last_id+1
return str(file_number)
else:
file_number=1
return str(file_number)
def getImageURL(instance, filename):
path = os.path.join("products_images/{}/product_image.png".format(getFileNumber()))
print(path)
if os.path.isfile("media/" + path):
print("image with this id already exist, ")
os.remove("media/" + path)
return path
else:
return path
class Product(models.Model):
name = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
availability = models.IntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2)
The following is the issue:
The instance doesn't have a primary key yet before it is created
When the instance gets saved in the database, then you can get the primary key
Maybe use signals, somehow? Do the logic it in the view after saving it?
Alternative option to pk:
You can generate a UUID for the object.
id = models.UUIDField(
primary_key = True,
default = uuid.uuid4,
editable = False)
Or alternatively:
Have a look at this package, you can set the uuid alphabet, length, and some more. This would allow you access to the id, for sure (same with uuid).
https://pypi.org/project/shortuuid/
id = ShortUUIDField(
length=16,
max_length=40,
prefix="id_",
alphabet="abcdefg1234",
primary_key=True,
)
Sidenote:
def getImageURL(self, filename): #Self would normally be defined as instance, but I suppose this is just semantics.
This is because the id is saved to the model after calling save(). The first guess would be to use save() to get the object in return. But save() does not return anything. So I put together a little example
class Thingy(models.Model):
name = models.CharField(
_('name'),
max_length=64,
)
def save(self, *args, **kwargs):
super(Thingy, self).save(*args, **kwargs)
print(self.pk)
My test was:
>>> t = Thingy(name="lol")
>>> t.save()
1
And it printed the primary key since save() mutades self. You could check if the pk exists before save(). If yes, just add the image path. If not. Execute save() first, manipulate the image path using pk, and save the object again. It is not elegant in any way, but I guess that is the only way.
I've defined some models in django and I can successfully add data to them using objects in django but the problem is that when I try to do this directly from the database it gives me this "HINT" There's a column named origin_id in the table flight, but it cannot be referenced from this part of the query, I'm using postgresql for the database, So can anyone please help me with this? I know there are similar questions like this but I couldn't find the solution.
class AirportManager(models.Manager):
#classmethod
def ret(cls):
return 'This is a custom "Manager"'
#classmethod
def code(cls):
obj = Airport.objects.all()
return obj
class Airport(models.Model):
code = models.CharField(max_length = 3)
city = models.CharField(max_length = 20)
objects = AirportManager()
class Meta:
db_table = 'airport'
def __str__(self):
return f"{self.city} ({self.code})"
class Flight(models.Model):
origin = models.ForeignKey(Airport,on_delete=models.CASCADE)
destination = models.ForeignKey(Airport,on_delete=models.CASCADE,related_name = "arrivals")
duration = models.IntegerField()
flights = models.Manager()
class Meta:
db_table = 'flight'
def __str__(self):
return f'from "{self.origin}" to "{self.destination}" in "{self.duration}" hrs'
I have two tables, which are connected with each other through a cross table. (Recipes <--> Ingredients)
My Serializer works ok, I can send POST-Requests and it saves everything. The problem ist, that every time a new Recipe comes in with let just say the Ingredient "Milk" then my Serializer creates a new entry in my database named Milk, although I have an already existing entry "Milk" in my database.
How do I tell my Serializer to use the Id of an already existing entry instead of creating a new one every time for the cross table.
Here is how I thought I could fix it, but it clearly doesn't:
class RecipeIngredientSerializer(serializers.ModelSerializer):
ingredient = IngerdientSerializer()
class Meta:
model = recipe_ingredients
fields = ['amount', 'unit', 'ingredient']
def create(self, validated_data):
ingredient_validated_data = validated_data.pop('ingredient')
ingredient_serializer = self.fields['ingredient']
ingredientDict = dict(ingredient_validated_data)
// This is where I try to check if there is already an ingredient with the name from the form
ingredientObj = ingredient.objects.all().filter(ingredient_name=ingredientDict['ingredient_name']).
if not ingredientObj:
ingredient_instance = ingredient.objects.create(**ingredientDict)
validated_data['ingredient'] = ingredient_instance
else:
ingredient_instance = ingredient_serializer.create(ingredientDict)
validated_data['ingredient'] = ingredient_instance
recipe_ingredients_instance = recipe_ingredients.objects.create(**validated_data)
return recipe_ingredients_instance
This code also seems to work, at least I find an existing ingredient, but after the last create() it seems to ignore what ever I push into the validated_data['ingredient'] object.
EDIT
my models are:
class recipe_ingredients(models.Model):
recipe = models.ForeignKey(recipe, models.CASCADE)
ingredient = models.ForeignKey(ingredient, models.CASCADE)
amount = models.IntegerField(default=0)
unit = models.CharField(max_length=50)
def __str__(self):
return self.ingredient.ingredient_name + ' of Recipe: ' + self.recipe.recipe_name
class recipe(models.Model):
recipe_name = models.CharField(max_length=200)
assembly_time = models.IntegerField(default=0)
number_of_servings = models.IntegerField(default=0)
tags = models.ManyToManyField(tag, blank=True)
def __str__(self):
return self.recipe_name
class ingredient(models.Model):
ingredient_name = models.CharField(max_length=200)
ingredient_calories = models.IntegerField('Calories per 100 Units', default=-1)
default_unit = models.CharField(max_length=50)
def __str__(self):
return self.ingredient_name
I got the answer, finally. My mistake is this line in my Serializer:
ingredientObj = ingredient.objects.all().filter(ingredient_name=ingredientDict['ingredient_name']).
if not ingredientObj:
ingredient_instance = ingredient.objects.create(**ingredientDict)
validated_data['ingredient'] = ingredient_instance
I changed it now so that it looks something like this:
ingredientObj = ingredient.objects.all().filter(ingredient_name=ingredientDict['ingredient_name']).
if len(ingredientObj):
ingredient_instance = ingredientObj.first()
validated_data['ingredient'] = ingredient_instance
The ingredient.object.create(**ingredientDict) does actually create a new object (who would have known ;) ). This is probably still an ugly solution and I am open to more criticism but this does work for now.
I have the following models in Django that have a structure as follows:
class Office_Accounts(models.Model):
accountid = models.EmailField(max_length=200, unique=True)
validtill = models.DateField(default=datetime.now)
limit = models.CharField(max_length=2)
class Device(models.Model):
device_type = models.ForeignKey(DeviceType,to_field='device_type')
serial_number = models.CharField(max_length=200,unique=True)
in_use_by = models.ForeignKey(User,to_field='username')
brand = models.CharField(max_length=200,default="-", null=False)
model = models.CharField(max_length=200,default="-", null=False)
type_number = models.CharField(max_length=200,blank=True,null=True, default = None)
mac_address = models.CharField(max_length=200,blank=True,null=True, default = None)
invoice = models.FileField(upload_to='Device_Invoice', null=True, blank = True)
msofficeaccount = models.ForeignKey(Office_Accounts, to_field="accountid")
class Meta:
verbose_name_plural = "Devices"
def full_name(self):
return self.device_type + self.serial_number + self.brand
I will display both of the models in admin.py.
Now, I want to display the count of each accountid present in the field "msofficeaccount" (present in Device Models) in my admin page of Office_Accounts model. For an example if xyz#abc.com appears in 10 rows of msofficeaccount field then, the count should be displayed as 10 in Office_Accounts admin page. Can anyone please guide me how should I approach this problem to solve it?
You could add a method to your admin class that returns the count of related devices for each office_account, but that would be very inefficient. Instead you can override get_queryset to annotate the count from a database aggregation function:
from django.db.models import Count
class Office_AccountsAdmin(admin.ModelAdmin):
list_display = (..., 'device_count')
...
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.annotate(device_count=Count('device'))
(On a minor note, Python style is always to use CamelCase for class names, and Django style is to use singular model names, so your model should really be called OfficeAccount.)
I have the following (simplified) data model:
class Article(Model):
uuid = models.CharField(primary_key=True, max_length=128)
class Attribute(Model):
uuid = models.CharField(primary_key=True, max_length=128)
article = models.ForeignKey(Article, related_name='attributes')
type = models.CharField(max_length=256)
value = models.CharField(max_length=256)
An example usage would be an article with an attribute attached to it with type="brand" and value="Nike". Now I want to write an API which can get all articles with a certain brand, but I can't seem to write the filter for it. This is what I have so far:
class PhotobookFilter(df.FilterSet):
brand = df.CharFilter(method='filter_brand')
class Meta:
model = Article
def filter_brand(self, queryset, name, value):
return queryset.filter('order__attributes')
class PhotobookViewSet(AbstractOrderWriterViewSet):
queryset = Article.objects.all()
serializer_class = ArticlePhotobookSerializer
filter_backends = (filters.DjangoFilterBackend,)
filter_class = PhotobookFilter
The line with queryset.filter is obviously not correct yet. I need to create a filter here that returns all articles that contain an attribute with type="brand" and value=value. How would I do this?
Are you sure you want to condense both lookups (type and value of Attribute) into one filter? Why not allow filtering on both fields separately?
E.g.
class PhotobookFilter(df.FilterSet):
type = df.CharFilter(method='filter_type')
value = df.CharFilter(method='filter_value')
class Meta:
model = Article
def filter_type(self, queryset, name, value):
return queryset.filter(**{'attributes__type': value})
def filter_value(self, queryset, name, value):
return queryset.filter(**{'attributes__value': value})
And now a query like ?type=brand&value=Nike should work.
Obviously you could condense both conditions into one filter and for example hard code the band part:
class PhotobookFilter(df.FilterSet):
brand = df.CharFilter(method='filter_brand')
def filter_brand(self, queryset, name, value):
return queryset.filter(**{'attributes__type': 'brand', 'attributes__value': value})
But keeping them separate feels way more flexible.
You could also filter in reverse like this:
class PhotobookFilter(df.FilterSet):
brand = df.CharFilter(method='filter_brand')
class Meta:
model = Article
def filter_brand(self, queryset, name, value):
articles = Attribute.objects.filter(type="brand", value=value).values_list('article_id', flat=True)
return queryset.filter(id__in=articles)
This will create subquery for Attribute, which will still be one sql request in the end
Use search_fields .For correct result rename your 'type' attribute name http://www.codesend.com/view/09ca65d42248fe1d89d07ce151f4f050/