When the form saves I want to save the object with a reference to the user's Profile page. So each profile can list every offer made by the user,
see Class based views query: get objects referenced by another model
ofertoj_oferto.profile_id may not be NULL
My model of "Oferto" contains a ForeignKey to the User's Profile. Right now I don't have an idea how i can tell the system "Get the logged in User's Profile and save this Ofeto with that ID"
Maybe there is some way where I lookup the profile associated with the user and don't need both user and profile on the Oferto model.
But I will still need to a list of every oferto made by the user on there profile
profiles.models
class Profile(BaseInfo):
bio = models.TextField(max_length=15000000)
user = models.ForeignKey(User)
views.py
class OfertoCreateView(LoginRequiredMixin, Turtle_CreateView):
model = Oferto
action = "created"
form_class = OfertoCreateForm
forms.py
class OfertoCreateForm(Turtle_Form):
class Meta:
model = Oferto
fields = ("name",
"description",
"tags",
"time",
"requirements",
"location",
"image",)
models.py
class Oferto(models.Model):
user = models.ForeignKey(User)
profile = models.ForeignKey(Profile)
name = models.CharField(max_length=150)
description = models.TextField(max_length=3000)
time = models.DecimalField(max_digits=10000000, decimal_places=2, null=True)
stelo = models.DecimalField(max_digits=10000000, decimal_places=2, null=True)
location = models.TextField(max_length=3000)
slug = AutoSlugField(('slug'), max_length=128, unique=True, populate_from=('name',))
tags = tagging.fields.TagField()
image = models.ImageField(upload_to='Ofertoj', blank=True, null=True)
requirements = models.TextField(max_length=550000, blank=True, null=True)
def get_absolute_url(self):
return reverse('oferto_detail', kwargs={'slug': self.slug})
def __unicode__(self):
return self.name
def get_tags(self):
return Tag.objects.get_for_object(self)
In your view/form/model save area: calling request.user returns a User object you can send to a model's ForeignKey field
Related
I'm calling the model_to_dict function and it is working,
but i want to be able to modify the results before returning.
The Author area is currently returning an id number and I want it to return the Username instead.
Does anybody know how to make that happen?
.model
class Comment(models.Model):
poopfact = models.ForeignKey(PoopFact, related_name="comments", on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
comment = models.TextField(unique=True, blank=True, null=True)
datetime = models.DateTimeField(auto_now_add=True)
likes = models.ManyToManyField(User, related_name="comment_likes")
.views
def comment(request, poopfact_id):
...
new_entry = Comment()
...
data = model_to_dict(new_entry)
return JsonResponse(data, safe=False)
Add this method to User model. By default user object represent itself as ID. Just replace it with username field in model (note: I am considering the 'username' is your User model field)
def __str__(self):
return self.username
I have the following models/forms/view in which I have managed to submit to two different models as follows:
Models
class Account(models.Model):
username = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=150)
actflag = models.CharField(max_length=1, blank=True)
acttime = models.DateTimeField(blank=True, null=True)
comments = models.TextField(_('comments'), max_length=500, blank=True)
def __str__(self):
return self.name
class ISIN(models.Model):
code = models.CharField(max_length=12)
account_name = models.ForeignKey(Account, on_delete=models.SET_NULL, null=True)
actflag = models.CharField(max_length=1, blank=True)
acttime = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.code
Forms
from apps.portfolio.models import Account, ISIN
class PortfolioForm(forms.ModelForm):
class Meta:
model = Account
fields = ['name', 'comments']
class IdentifierForm(forms.ModelForm):
class Meta:
model = ISIN
fields = ['code']
View
def portfolios(request):
if request.user.is_authenticated:
if request.POST:
fm = PortfolioForm(request.POST)
fm2 = IdentifierForm(request.POST)
if fm.is_valid():
messages.success(request, 'Portfolio has been created.')
account = fm.save(commit=False)
account.username = request.user
account.acttime = timezone.now()
account.actflag = 'I'
account.save()
isin = fm2.save(commit=False)
#isin.account_name = account.name
isin.acttime = timezone.now()
isin.actflag = 'I'
isin.save()
return redirect('portfolios')
else:
fm = PortfolioForm()
fm2 = IdentifierForm()
context = {"name": request.user, "form": fm, "form2": fm2}
return render(request, 'portfolios.html', context)
else:
return redirect('login')
However, you will notice the commented line in my view: isin.account_name = account.name, when I uncomment this line and try to submit the forms again I get the following error: Cannot assign "'test'": "ISIN.account_name" must be a "Account" instance.
I believe it's to do with ForeignKey but still unsure how to store the newly created account name the user submitted within the isin model.
Help is much appreciated.
Although my answer solves the problem you originally had, there are a couple additional points that I wanted to make.
Improve naming and fix the original error
Your field is called account_name, and it implies that a string will be stored there. If it was actually a string, you would be able to do what you tried:
isin.account_name = account.name
In reality, you have a ForeignKey to the Account model, so you have to actually save a reference to the account object:
isin.account_name = account
It's a really good idea to have a foreign key instead of just a string because it avoids denormalization.
The problem here is the name of the field, account_name. If you later want to access the account name, you would have to write something like isis.account_name.name. Sounds wrong, doesn't it?
You could solve this by renaming your field like so:
class ISIN(models.Model):
code = models.CharField(max_length=12)
account = models.ForeignKey(Account, on_delete=models.SET_NULL, null=True)
actflag = models.CharField(max_length=1, blank=True)
acttime = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.code
Then, in your view, you would just isin.account = account, and later, if you wanted to access the name, you would use isin.account.name.
Another minor thing is that in some places an account is called Account and in other places it's Portfolio. This creates an illusion that they're unrelated entities and makes your code harder to read and maintain.
You probably should decide which one is the better term, and make it consistent everywhere.
Use builtin timestamp mechanism
Looks like you're using the acttime field to manually store creation time of accounts and ISINs.
You could use Django's auto_now_add property to do that automatically, like so:
class Account(models.Model):
acttime = models.DateTimeField(auto_now_add=True)
If you also wanted to store the last time an Account was updated, you could use auto_now (also renamed fields here for clarity):
class Account(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
And to stay DRY, you could make a mixin for that and use it in both Account and ISIN:
class TimeStampMixin(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Account(TimeStampMixin, models.Model):
username = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=150)
actflag = models.CharField(max_length=1, blank=True)
comments = models.TextField(_('comments'), max_length=500, blank=True)
def __str__(self):
return self.name
class ISIN(TimeStampMixin, models.Model):
code = models.CharField(max_length=12)
account = models.ForeignKey(Account, on_delete=models.SET_NULL, null=True)
actflag = models.CharField(max_length=1, blank=True)
def __str__(self):
return self.code
This way, the creation time and the latest update time are automatically stored in your models (the ones that inherit from TimeStampMixin).
Validate both forms
Looks like you're only checking one of the forms for validity, and not the other:
if fm.is_valid():
You should probably check both, in case ISIN.code is invalid:
if fm.is_valid() and fm2.is_valid():
What it means is that you have to make an instance of the account model by getting the name in order to save the form like so:
def portfolios(request):
if request.user.is_authenticated:
if request.POST:
fm = PortfolioForm(request.POST)
fm2 = IdentifierForm(request.POST)
if fm.is_valid():
messages.success(request, 'Portfolio has been created.')
account = fm.save(commit=False)
account.username = request.user
account.acttime = timezone.now()
account.actflag = 'I'
account.save()
# Here is where we get the instance of account
account = Account.objects.get(name=account.name)
isin = fm2.save(commit=False)
isin.account_name = account
isin.acttime = timezone.now()
isin.actflag = 'I'
isin.save()
return redirect('portfolios')
else:
fm = PortfolioForm()
fm2 = IdentifierForm()
context = {"name": request.user, "form": fm, "form2": fm2}
return render(request, 'portfolios.html', context)
else:
return redirect('login')
The field account_name is a ForeignKey to Account, but you are assigning an string. You should to assign an Account.
Change:
isin.account_name = account.name
To:
isin.account_name = account
So I have users whose profiles are automatically created after user creation with the help of Django Signals
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class Meta:
db_table = "Profile"
class Liked_Songs(models.Model):
track_name = models.ForeignKey(Profile , on_delete= models.CASCADE , related_name="track")
artiste_name= models.ForeignKey(Profile, on_delete= models.CASCADE , related_name="artiste")
album_name = models.ForeignKey(Profile, on_delete= models.CASCADE, related_name= "album")
class Meta:
db_table = "Liked Songs"
def __str__(self):
return self.track_name
In this Liked_Songs model, my views.py accesses an API and my aim is to allow all fields in that model to be populated with data from the API. So there will be multiple track_name etc received. So each Profile can have many track names etc. Is the ForeignKey appropriate for this?
However, when I use this route below, i get an error I have stated in the problem description.
Views.py
def liked(request):
try:
if "access_token" in request.session:
sp = Spotify(auth = request.session.get("access_token"))
liked = sp.current_user_saved_tracks(limit=30)['items']
for idx, item in enumerate(liked):
track = item['track']["name"]
artist= item["track"]["artists"][0]["name"]
album = item["track"]["album"]["name"]
Liked_Songs.objects.create(
track_name= track,
artiste_name= artist,
album_name = album
).save()
except SpotifyException:
return redirect(reverse("login"))
you are storing profile id in track so you cannot pass name in it so try to pass profile id in it
try this track_name_id = profile_id
I have an updateview in which a manager can go and edit all the fields for the associate. Looks like this:(requirement is to add associate_mgr in the as a dropdown in the updateview)enter image description here
views.py
class ReallocationTeam(LoginRequiredMixin,UpdateView):
model = UserDetails
form_class = ViewEditSample
def get_success_url(self):
return reverse('UserProfile:index')
forms.py
class ViewEditSample(ModelForm):
class Meta:
model = UserDetails
fields = ['associate_name','client','lob','associate_mgr']
The manager should be able to edit the "assciate_mgr" of that associate too.
models.py
associate_name = models.CharField(max_length=200)
associate_nbr = models.CharField(max_length=8, primary_key=True)
associate_email = models.EmailField()
associate_department_id = models.CharField(max_length=50)
associate_mgr = models.CharField(max_length=100,blank=True, null=True)
associate_exec = models.CharField(max_length=100,blank=True, null=True)
associate_org = models.CharField(max_length=100,blank=True,null=True)
title = models.CharField(null=True, blank=True, max_length=100)
date_of_service = models.CharField(null=True,blank=True,max_length=11)
is_manager = models.BooleanField(default=False)
is_exec = models.BooleanField(default=False)
is_team_lead = models.BooleanField(default=False)
but associate_mgr is not a choice field in my db.
I need to add a dropdown that contains associate_mgr in my UpdateView. How do I go about implementing that?
Should I go about writing a query to get all managers and populate them i a dropdow: like this mgr = UserDetails.objects.filter(is_manager=True) But then how do i store the selected in associate_mgr field in db?
You can override your form field in your ModelForm to be a ChoiceField with a list of choices: UserDetails.objects.filter(is_manager=True).values_list('name').
associate_mgr = forms.ChoiceField(choices=
UserDetails.objects.filter(is_manager=True).values_list('associate_name', 'associate_name')
)
Then the choice will automatically be saved (the 'associate_name' field value).
But it would probably be a better idea to use a ForeignKey on your model, rather than a CharField. That would enforce the values to be other UserDetails rather than just a string.
I the following in the models.py:
class Item(models.Model):
date = models.DateField(_('date'), blank=True, null=True)
description = models.CharField(_('description'), max_length=255)
content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
object_id = models.PositiveIntegerField(_('object id'), db_index=True)
object = generic.GenericForeignKey('content_type', 'object_id')
class ItemAccountAmountRef(Item):
""" Items of which a Quote or an Invoice exists. """
amount = models.DecimalField(max_digits=10, decimal_places=2)
reference = models.CharField(max_length=200)
debit_account = models.ForeignKey(Account, related_name='receivables_receipt_debit_account')
credit_account = models.ForeignKey(Account, related_name='receivables_receipt_credit_account')
class PaymentItem(ItemAccountAmountRef):
pass
class Payment(models.Model):
invoice = models.ManyToManyField(Invoice, null=True, blank=True)
date = models.DateField('date')
attachments = generic.GenericRelation(Attachment)
site = models.ForeignKey(Site, related_name='payment_site', null=True, blank=True
items = generic.GenericRelation(PaymentItem)
in the admin.py:
class PaymentItemInline(generic.GenericTabularInline):
model = PaymentItem
form = PaymentItemForm
class PaymentAdmin(admin.ModelAdmin):
inlines = [PaymentItemInline]
in forms.py:
class PaymentItemForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PaymentItemForm, self).__init__(*args, **kwargs)
self.fields['credit_account'].label = "Bank Account"
In the PaymentItemInline the label is not changing. I have tried changing other attributes e.g. class which work. If I run through the init in debug mode I can see that the label variable is changing however when the form is rendered the field is still labelled credit account. Any suggestions?
You're 98% of the way there. Instead of trying to futz with the form field in __init__, just redefine it in your ModelForm. If you name it the same thing, django will be able to figure out that it is supposed to validate & save to the ForeignKey field. You can use the same formula to change a Field or Widget completely for a given field in a ModelForm.
You can find the default form field types for each model field type here: https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#field-types
class PaymentItemForm(forms.ModelForm):
credit_account = forms.ModelChoiceField(label="Bank Account", queryset=Account.objects.all())
That's it. No need to override any functions at all : )
Incidentally, the docs for this field are here: https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelchoicefield