Django order_by not working properly - python

I'm trying to get all the conversations ordered by it last message, but when I use the order_by clausule, the conversations are repeated.
Query without order_by:
conversaciones = Conversacion.objects.filter(usuarios=request.user)
Result (Grouped by Conversations but not ordered by the most recent last message first):
Query with order_by:
conversaciones = Conversacion.objects.filter(usuarios=request.user).order_by('-mensaje__fechaEnvio')
Result:
My models.py:
class Mensaje(models.Model):
remitente = models.ForeignKey('Usuario', on_delete=models.CASCADE, related_name='remitente')
destinatario = models.ForeignKey('Usuario', on_delete=models.CASCADE, related_name='destinatario')
cuerpo = models.TextField(validators=[MaxLengthValidator(750)])
leido = models.BooleanField(default=False)
fechaEnvio = models.DateTimeField(auto_now_add=True)
conversacion = models.ForeignKey('Conversacion', on_delete=models.CASCADE)
class Meta:
ordering = ['-fechaEnvio']
def __str__(self):
return str(self.remitente) + ' -> ' + str(self.destinatario)
class Conversacion(models.Model):
usuarios = models.ManyToManyField('Usuario', related_name='usuarios')
agresion = models.ForeignKey('Agresion', on_delete=models.CASCADE)
#property
def ultimoMensaje(self):
return self.mensaje_set.latest('fechaEnvio')

I found a solution:
conversaciones = Conversacion.objects.filter(usuarios=request.user).annotate(max_fecha=Max('mensaje__fechaEnvio')).order_by('-max_fecha')
I'm using MySQL so I can't use distinct with params.

As #jota suggested, I can add something in your model Mesanje. Make ordering a tuple and don't forget to add a comma and don't forget to make migrations again.
class Mesanje(models.Model):
........
class Meta:
ordering = ('-fechaEnvio',)

Related

Serialize QuerySet to JSON with FK DJANGO

I want to send a JSON of a model of an intersection table so I only have foreign keys saved, I tried to make a list and then convert it to JSON but I only receive the ids and I need the content, I also tried in the back as a temporary solution to make a dictionary with the Queryset but the '<>' makes it mark an error in the JS, does anyone know a way to have the data of my foreign keys and make them a JSON?
models:
class Periodos(models.Model):
anyo = models.IntegerField(default=2022)
periodo = models.CharField(max_length=10)
fecha_inicio = models.DateField(blank=True, null=True)
fecha_fin = models.DateField(blank=True, null=True)
class Meta:
app_label = 'modelos'
verbose_name = u'periodo'
verbose_name_plural = u'Periodos'
ordering = ('id',)
def __str__(self):
return u'%s - %s' % (self.anyo,self.periodo)
class Programas(models.Model):
programa = models.CharField(max_length=255,blank=True, null=True)
activo = models.BooleanField(default=True)
class Meta:
app_label = 'modelos'
verbose_name = u'Programas'
verbose_name_plural = u'Programas'
def __str__(self) -> str:
return self.programa
class Programa_periodo(models.Model):
periodo = models.ForeignKey(Periodos, related_name='Programa_periodo_periodo',on_delete=models.CASCADE)
programa = models.ForeignKey(Programas, related_name='Programa_periodo_Programa',on_delete=models.CASCADE)
class Meta:
app_label = 'modelos'
verbose_name = u'Programa Periodo'
verbose_name_plural = u'Programa Periodo'
def __str__(self) -> str:
return self.programa.programa
py where i send data
def iniciativa(request):
if request.user.is_authenticated:
context = {}
context['marcas'] = json.dumps(list(Marcas.objects.values()))
context['eo'] = get_estructura_org()
#This is where I call the data
programa = Programa_periodo.objects.all()
#These two only return the ids
# context['programa_periodos'] = json.dumps(list(Programa_periodo.objects.values()))
#context['programa_periodos'] = serializers.serialize("json", Programa_periodo.objects.all())
#One of my try but fail for the '<>'
programa_periodo = {}
for pg in programa:
programa_periodo[pg.periodo] = pg.programa
context['programa_periodos'] = programa_periodo
return render(request, 'agregar_iniciativa.html', context)
else:
return HttpResponseBadRequest('Favor de ingresar sesiĆ³n en el sistema.', format(request.method), status=401)
I am not sure that I get the question right, but if you need a special field value from foreign key you can use smth like:
Programa_periodo.objects.values("id", "periodo__periodo", "programa__programa")
With double underscore. Try it in the shell first. Check the docs here https://docs.djangoproject.com/en/4.0/ref/models/querysets/#values

How can I make DRF Serializer create() function only create an entry that does not exist yet?

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.

Django: Adding more fields to each ManyToMany Field option

Is It possible to add one or more Char Fields to each ManyToMany field option?
My Models:
class engineeringUni(models.Model):
field2 = models.CharField(max_length=200)
des_eng = models.CharField(max_length=1000, default='Add description')
def __str__(self):
return self.field2
def description_eng_universities(self):
return self.des_eng
class engineering_courses(models.Model):
course_name = models.CharField(max_length=400)
course_description = models.CharField(max_length=1000, default='This is a description')
course_offered_by = models.ManyToManyField(engineeringUni, related_name='course_offered_by')
course_duration = models.IntegerField(blank=False, default='2')
def __str__(self):
return self.course_name
def description_course(self):
return self.course_description
def offered_by_courses(self):
return self.course_offered_by
def duration_courses(self):
return str(self.course_duration)
As you can see in the image, I have the options in the ManyToMany field. Those options are:
University 1
University 2
University 3
What I want to have is an additional text (Char) field next to each of these options (University 1, University 2, University 3).
Is this possible?
EDIT 1:
Current code:
class engineering_courses(models.Model):
course_name = models.CharField(max_length=400)
course_description = models.CharField(max_length=1000, default='This is a description')
course_offered_by = models.ManyToManyField(
engineeringUni,
through='ThroughModel',
through_fields=('course', 'university'),
)
course_duration = models.IntegerField(blank=False, default='2')
def __str__(self):
return self.course_name
def description_course(self):
return self.course_description
def offered_by_courses(self):
return self.course_offered_by
def duration_courses(self):
return str(self.course_duration)
class ThroughModel(models.Model):
course = models.ForeignKey(engineering_courses, on_delete=models.CASCADE)
university = models.ForeignKey(engineeringUni, on_delete=models.CASCADE)
additional_text = models.CharField(max_length=200)
EDIT 2: Problem fixed. I was getting that no table error because I had deleted the migration files and on deleting database (db.sqlite3) file and applying migration again, It fixed.
You can use a through model in the ManyToManyField (docs). This model can be used to store any additional fields.
class engineering_courses(models.Model):
# ...
course_offered_by = models.ManyToManyField(engineeringUni, related_name='course_offered_by', through='ThroughModel')
class ThroughModel(models.Model):
course = models.ForeignKey(engineering_courses)
university = models.ForeignKey(engineeringUni)
additional_text = models.CharField()
Take another look at the django docs referenced in the answer from arjun27. You have more than one foreign key in your ThroughModel, so django is confused. Try specifying the through fields in your engineering_course model, migrate the changes, and see if that works.
Mark

DRF oneToOneField Create Serializer

trying to do some nested model creation with drf/create serizlizer.
what i'm trying to serialize is 'TradePost' model, which has post, and ProductItem in it.
i already have 'ProductItemSerializer', and 'PostSerializer' by using this.. how can i serialize them?? with creation? not by telling existing record with pk value.
models.py
class ProductItem(models.Model):
baseProduct = models.ForeignKey(Product, related_name='baseProduct')
seller = models.ForeignKey(User)
price = models.DecimalField(max_digits=6, decimal_places=2)
isOrderClosed = models.BooleanField()
isTradeCompleted = models.BooleanField()
def __str__(self):
return '[seller = '+self.seller.username+']' + '[product = '+(str)(self.baseProduct)+']' + '[id = '+(str)(self.id)+']'
class TradePost(models.Model):
basePost = models.OneToOneField(Post)
baseProductItem = models.OneToOneField(ProductItem)
def __str__(self):
return '[post = ' + (str)(self.basePost) + ']' + '[product = ' + (str)(self.baseProductItem) + ']' + '[id = ' + (str)(self.id) + ']'
in serialziers.py
class ProductItemCreateSerializer(serializers.ModelSerializer):
class Meta:
model = ProductItem
fields = ('baseProduct', 'price')
#???
class TradePostCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TradePost
fields = ('basePost', 'baseProductItem',)
def create(self, validated_data):
post =
Similar to the writable nested serializer, you can try something like this:
class TradePostCreateSerializer(serializers.ModelSerializer):
basePost = PostCreateSerializer()
baseProductItem = ProductItemCreateSerializer()
class Meta:
model = TradePost
fields = ('basePost', 'baseProductItem',)
def create(self, validated_data):
# pop out the dict to create post and item, depend on whether you want to create post or not
post = validated_data.get('basePost')
product = validated_data.get('baseProductItem')
# create post first
trade_post = None
post_obj = Post.objects.create(**post)
if post_obj:
# create product item
prod_item = ProductItem.objects.create(basePost=post_obj, **product)
trade_post = TradePost.objects.create(baseProduct=prod_item, **validated_data)
return trade_post
For python, please follow naming convention and use lower_case_with_underscores like base_product, base_post, it will be much easier to read
You can use serializers to represent field, For eg,
class TradePostCreateSerializer(serializers.ModelSerializer):
basePost = PostSerializer()
baseProductItem = ProductItemSerializer()
class Meta:
model = TradePost
fields = ('basePost', 'baseProductItem',)
Reference:
DRF Nested relationships
If you're looking for writable nested serializers
Writable nested serializers

Django update MySQL database issue involving foreign keys

Good morning all,
I have the following two models:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class StraightredTeam(models.Model):
teamid = models.IntegerField(primary_key=True)
teamname = models.CharField(max_length=36)
country = models.CharField(max_length=36,null=True)
stadium = models.CharField(max_length=36,null=True)
homepageurl = models.CharField(max_length=36,null=True)
wikilink = models.CharField(max_length=36,null=True)
teamcode = models.CharField(max_length=5,null=True)
teamshortname = models.CharField(max_length=24,null=True)
currentteam = models.PositiveSmallIntegerField(null=True)
def natural_key(self):
return self.teamname
class Meta:
managed = True
db_table = 'straightred_team'
class StraightredFixture(models.Model):
fixtureid = models.IntegerField(primary_key=True)
home_team = models.ForeignKey('straightred.StraightredTeam', db_column='hometeamid', related_name='home_fixtures')
away_team = models.ForeignKey('straightred.StraightredTeam', db_column='awayteamid', related_name='away_fixtures')
fixturedate = models.DateTimeField(null=True)
fixturestatus = models.CharField(max_length=24,null=True)
fixturematchday = models.IntegerField(null=True)
spectators = models.IntegerField(null=True)
hometeamscore = models.IntegerField(null=True)
awayteamscore = models.IntegerField(null=True)
homegoaldetails = models.TextField(null=True)
awaygoaldetails = models.TextField(null=True)
hometeamyellowcarddetails = models.TextField(null=True)
awayteamyellowcarddetails = models.TextField(null=True)
class Meta:
managed = True
db_table = 'straightred_fixture'
The following view works perfectly:
#csrf_exempt
def updateteams(request):
if request.user.is_authenticated():
xmlsoccer = XmlSoccer(api_key='XYZ', use_demo=True)
teams = xmlsoccer.call_api(method='GetAllTeams')
numberOfTeamsUpdated = 0
for team in teams:
if 'Team_Id' in team.keys():
teamUpdate = StraightredTeam(teamid=team['Team_Id'],teamname=team['Name'],stadium=team['Stadium'])
teamUpdate.save()
numberOfTeamsUpdated = numberOfTeamsUpdated + 1
return HttpResponse(str(numberOfTeamsUpdated) + " team/s have been added/updated.")
else:
return HttpResponse("You must be logged in to update teams.")
However, the following view does not work:
#csrf_exempt
def updatefixtures(request):
if request.user.is_authenticated():
xmlsoccer = XmlSoccer(api_key='XYZ', use_demo=True)
fixtures = xmlsoccer.call_api(method='GetFixturesByLeagueAndSeason',
seasonDateString='1516',
league='Scottish Premier League')
numberOfFixturesUpdated = 0
for fixture in fixtures:
if 'Id' in fixture.keys():
fixtureUpdate = StraightredFixture(fixtureid=fixture['Id'],away_team=fixture['AwayTeam_Id'],home_team=fixture['HomeTeam_Id'])
fixtureUpdate.save()
numberOfFixturesUpdated = numberOfFixturesUpdated + 1
return HttpResponse(str(numberOfFixturesUpdated) + " fixture/s have been added/updated.")
else:
return HttpResponse("You must be logged in to update teams.")
When I try to call it I get the following error:
Cannot assign "'54'": "StraightredFixture.home_team" must be a
"StraightredTeam" instance.
54 id the correct teamid that needs to be entered. 54 is definitely in the teamdid of the straightred_fixture table.
I assume it is something to do with the foreign key as anything that is not a foreign key seems to work perfectly. I am sure I have a simple syntax issue somewhere.
Many thanks for any help that may be give, Alan.
You probably upgraded from some older Django version?
If so, note that now, when assigning foreign keys, you must assign a saved instance of the foreign object. If you want to just assign by id, then use <fieldname>_id
I believe, here,
fixtureUpdate = StraightredFixture(
fixtureid=fixture['Id'],
away_team_id=fixture['AwayTeam_Id'],
home_team_id=fixture['HomeTeam_Id']
)
...should do the trick.

Categories

Resources