I want to automatically generate the "path" field of my model when I use the create method of my serializer. Otherwise I would like to be able to pass this field in my request to be able to modify it later.
I haven't found a way to make a single field of my template optional.
Here is my model :
models.py
class Shop(models.Model):
name = models.CharField(max_length=255)
category = models.ForeignKey(ShopCategory, on_delete=models.SET_NULL, null=True, blank=True)
description = models.TextField(blank=True, null=True)
path = models.CharField(max_length=255, unique=True)
mustBeLogged = models.BooleanField(default=False)
deliveries = models.FloatField(default=7)
def __str__(self):
return self.name
Here is my code :
serializer.py
class ShopSerializer(serializers.ModelSerializer):
class Meta:
model = Shop
exclude = ['path']
def create(self, validated_data):
path = validated_data["name"].replace(" ", "-").lower()
path = unidecode.unidecode(path)
unique = False
while unique == False:
if len(Shop.objects.filter(path=path)) == 0:
unique = True
else:
# Generate a random string
char = "abcdefghijklmnopqrstuvwxyz"
path += "-{}".format("".join(random.sample(char, 5)))
shop = Shop.objects.create(**validated_data, path=path)
shop.save()
return shop
views.py
def post(self, request):
"""For admin to create shop"""
serializer = ShopSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
You can set path to be accepted as Null and pop it in create method. In this way you won't have to add it in exclude.
class ShopSerializer(serializers.ModelSerializer):
path = serializers.CharField(allow_blank=True, allow_null=True)
class Meta:
model = Shop
fields = '__all__'
def create(self, validated_data):
validated_data.pop('path')
path = validated_data["name"].replace(" ", "-").lower()
path = unidecode.unidecode(path)
unique = False
while unique == False:
if len(Shop.objects.filter(path=path)) == 0:
unique = True
else:
# Generate a random string
char = "abcdefghijklmnopqrstuvwxyz"
path += "-{}".format("".join(random.sample(char, 5)))
shop = Shop.objects.create(**validated_data, path=path)
shop.save()
return shop
def update(self, instance, validated_data):
#You will have path in validated_data
#And you may have to check if the values are null
return super(ShopSerializer, self).update(instance, validated_data)
Apart from this, in your code:
while unique == False:
if len(Shop.objects.filter(path=path)) == 0:
This would be very costly operation. In every loop it would fire a query, I'm not sure about the logic behind it but you may want to change this.
Related
I am using the django-import-export package. I need, everytime the user send a .csv table, to create a column in the .csv file and fill every row with the logged user. What I did is:
In my resources.py file:
class ProductListResource(resources.ModelResource):
class Meta:
model = ProductList
skip_unchanged = True
report_skipped = True
exclude = ('id',)
import_id_fields = ('sku',)
In my models.py file:
class ProductList(models.Model):
sku = models.CharField(primary_key=True, max_length=200)
client = models.ForeignKey(get_user_model(), default=1, on_delete=models.CASCADE)
name = models.CharField(max_length=256)
description = models.CharField(max_length=1000)
storage = models.CharField(max_length=256)
cost_price = models.CharField(max_length=256)
sell_price = models.CharField(max_length=256)
ncm = models.CharField(max_length=256)
inventory = models.IntegerField(null=True)
And finally in my views.py file:
def simple_upload(request):
if request.method == 'POST':
product_resource = ProductListResource()
product_resource.Meta.model.client = request.user.id
dataset = Dataset()
new_product_table = request.FILES['myfile']
dataset.load(new_product_table.read().decode('utf-8'), format='csv')
result = product_resource.import_data(dataset, dry_run=True) # Test the data import
if not result.has_errors():
product_resource.import_data(dataset, dry_run=False) # Actually import now
context = {'product_list': ProductList.objects.filter(client=request.user.id).all()} #.filter(client=request.user.username)
return render(request, 'Clientes/import.html', context)
My problem is that in the table appears that the value was changed, but if I click in the object, at the admin's page, the user that is selected is the first one.
The solution to that problem was simply that I was looking to change the wrong variable. To fix this and add another column to the dataset I did the following:
def simple_upload(request):
if request.method == 'POST':
product_resource = ProductListResource()
dataset = Dataset()
new_product_table = request.FILES['myfile']
dataset.load(new_product_table.read().decode('utf-8'), format='csv')
dataset.append_col([request.user.id]*dataset.height, header='client') # This code add another column to the imported csv.
result = product_resource.import_data(dataset, dry_run=True) # Test the data import
if not result.has_errors():
product_resource.import_data(dataset, dry_run=False) # Actually import now
context = {'product_list': ProductList.objects.filter(client=request.user.id).all()} #.filter(client=request.user.username)
return render(request, 'Clientes/import.html', context)
There are two models and I would like to store a column field from one model to another model.
models.py:
class Company(models.Model):
name = ...
def __str__(self):
return self.name
class Rate(models.Model):
company = models.ForeignKey(Company)
currency = ...
name = ...
def __str__(self):
return self.name + " " + self.currency
class Client(models.Model):
name = ...
currency = ....
company = models.ForeignKey(Company)
base_rate = models.ForeignKey(Rate)
def __str__(self):
return self.name
forms.py:
class ClientForm(forms.ModelForm):
class Meta:
model = Client
fields = (
"name",
"company",
"base_rate", )
views.py:
class ClientCreateView(FormView):
template_name = "client/new_package.html"
form_class = ClientForm
success_url = reverse_lazy("home")
def form_valid(self, form):
detail = form.save(commit=False)
base_rate_id = form.cleaned_data['base_rate']
detail.currency = Rate.objects.values_list("currency", flat=True).filter(base_rate_id=base_rate_id)
detail.save()
if detail is not None:
return redirect('display_package' , detail.id)
else:
return super(ClientCreateView, self).form_valid(form)
Basically, I want selected currency value to be saved in my client model from Rate model. Any help will be appreciated as from cleaned data I am getting str returned value instead of selected option id value (i.e. str from client model ).
I'm not really sure why you want that. But more to the point, why you are doing it with filter and values_list? You need to get the value and store it.
detail = form.save(commit=False)
detail.currency = form.cleaned_data['base_rate'].currency
detail.save()
I was wondering if there is a way that I can alter a model form within the views.py file to create a multiple choice dropdown field for form choices. I want to set each option on the choice field from the results of a queryset.
for example:
I want to from_acct field to have a scroll down option with the following list..
wells fargo
chase
tabz
bank of america
the list of banks are results of a query set
Here is what i have so far in the views.py file.
form = TransferForm()
form.fields['from_acct'].queryset = Accounts.objects.filter(user = currentUser).all()
message = 'please fill out the below form'
parameters = {
'form':form,
'currentUser':currentUser,
'message':message,
}
return render(request, 'tabs/user_balance.html', parameters)
here is the forms.py file
class TransferForm(forms.ModelForm):
class Meta:
model = Transfers
fields = ['from_acct', 'to_acct', 'amount', 'memo']
labels = {
'from_acct':'from',
'to_acct':'to',
}
here is the model.py file
class Transfers(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
from_acct = models.CharField(max_length=150, default='account')
to_acct = models.CharField(max_length=150, default='accont')
amount = models.DecimalField(decimal_places=2, max_digits=9, default=0)
memo = models.CharField(max_length=200, default='memo')
frequency = models.SmallIntegerField(default=1)
status = models.SmallIntegerField(default=1)
create = models.DateTimeField(auto_now_add=True)
You can try to set choices arg for CharField by function.
Like that:
class Transfers(models.Model):
field = models.CharField(max_length=255, choices=result_query())
def result_query(self):
# you can use that with self if u need transfers.pk for querying
return Something.objects.exclude(bank_id__in=[bank.id for bank in self.banks.all())
def result_query():
# or there if not
return Something.objects.filter(any_field__gte=123)
For sure, you can realize any logic in the function, so you can dynamically change options for char field.
UPDATE:
Sure, u haven't pass request into the function.
That should be like that:
view.py:
def my_view(request):
if request.method == 'GET':
form = TransferForm(user=request.user)
...
return something here
forms.py
class TransferForm(ModelForm):
class Meta:
model = Transfer
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(TransferForm, self).__init__(*args, **kwargs)
self.fields['accounts'].choices = Accounts.objects.filter(user = currentUser).all()
When trying to add a second appointment (for the same date) which has a dayplan foreign key using ModelForm and CreateView, unique constraint fails due to DayPlan having 'date' field as unique.
This issue is not present using the django-admin create form.
I tried to remove the unique=True from dayplan.date to see what happens -> every time i add an appointment, even if dayplan.date exist, a new dayplan is created.
the issue seems to be related to these 2 line:
daydate = DayPlan.objects.filter(date=planned_date)
form.cleaned_data['dayplan'] = daydate
The code is here:
class DayPlan(models.Model):
date = models.DateField(unique=True, db_index=True)
comment = models.TextField(null=True, blank=True)
def __str__(self):
return 'Planning voor {}'.format(self.date)
def get_absolute_url(self):
return reverse('organizer_dayplan_detail', kwargs={'pk': self.pk})
class Appointment(models.Model):
comment = models.CharField(null=True, blank=True, max_length=255)
planned_date = models.DateField()
doctor = models.ForeignKey(Doctor)
visited = models.BooleanField(default=False)
dayplan = models.ForeignKey(DayPlan)
class AppointCreate(CreateView):
model = Appointment
form_class = AppointmentForm
template_name = 'organizer/organizer_appointment_create.html'
# initial = {'doctor': 'pk', 'comment': 'test',}
def get_initial(self):
return {
"doctor": self.request.GET.get('doctor')
}
def form_valid(self, form):
planned_date = form.cleaned_data['planned_date']
try:
daydate = DayPlan.objects.filter(date=planned_date)
form.cleaned_data['dayplan'] = daydate
form.instance.save()
except:
daydate = DayPlan.objects.create(date=planned_date)
form.instance.dayplan = daydate
form.instance.save()
return super(AppointCreate, self).form_valid(form)
class AppointmentForm(forms.ModelForm):
class Meta:
model = Appointment
fields = {'comment', 'planned_date', 'doctor', 'visited', 'dayplan'}
widgets = {'visited': forms.HiddenInput(),}
exclude = {'dayplan',}
P.S. i do realize that i don't need to use "form.instance.save()" here. removing them has no effect.
Thanks in advance!
solved
daydate, created = DayPlan.objects.get_or_create(date=planned_date)
form.instance.dayplan = DayPlan.objects.get(date=planned_date)
I'm trying to map a foreign key to POST data when creating a new object through a serializer. There are two foreign keys in the object, one is serializing perfectly, the other is creating an error.
Model:
class Event(models.Model):
owner = models.ForeignKey('auth.User', related_name='owner', blank=True)
date = models.DateField('eventdate')
time = models.TimeField('eventtime', default=now)
eventtype = models.ForeignKey(EventType, related_name='eventtype', blank=True)
# duration = models.DurationField()
location = models.CharField(max_length=200, blank=True)
attenders = models.ManyToManyField(User, related_name='attenders')
invited = models.ManyToManyField(User, related_name='invitedlist')
View:
class EventMixin(RetrieveUpdateDestroyAPIView, CreateAPIView):
serializer_class = EventSerializer
def get_queryset(self):
return Event.objects.all()
def partial_update(self, request, *args, **kwargs):
request['owner'] = request.user
sname = request['eventtype']
request['eventtype'] = EventType.objects.filter(sname=sname)
json_str = json.dumps(self.request.data)
data = json.loads(json_str)
try:
invited = list(data['toInvite'])
for i in invited:
for j in User.objects.filter(username=i):
invite = EventInvite(invited=j, sender=request.user, event=self.get_object())
invite.save()
self.get_object().invited.add()
except KeyError:
pass
return super(EventMixin, self).partial_update(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
new = {}
new['owner'] = request.user.__dict__
new['date'] = request.data['date']
new['time'] = request.data['time']
new['location'] = request.data['location']
sname = request.data['eventtype']
new['eventtype'] = EventType.objects.get(sname=sname).__dict__
json_str = json.dumps(self.request.data)
data = json.loads(json_str)
serializer = EventMixinSerializer(data=new)
with open('/tmp/log.txt', 'w+') as f:
f.write(str(serializer.is_valid()))
f.write(str(serializer.validated_data))
f.close()
serializer.is_valid();
serializer.save()
try:
invited = list(data['toInvite'])
for i in invited:
for j in User.objects.filter(username=i):
invite = EventInvite(invited=j, sender=request.user, event=self.get_object())
invite.save()
self.get_object().invited.add()
except KeyError:
pass
Serializer:
class EventMixinSerializer(serializers.ModelSerializer):
owner = UserSerializer(read_only=True)
eventtype = EventTypeSerializer()
attenders = FriendsListingField(read_only=True)
invited = FriendsListingField(read_only=True)
class Meta:
model = Event
fields = ('owner', 'eventtype', 'date', 'time', 'location', 'id', 'attenders', 'invited')
def create(self, validated_data):
owner = validated_data.pop('owner')
owner = owner.instance
eventtype = validated_data.pop('eventtype')
eventtype = eventtype.instance
event = Event.objects.create(owner=owner, eventtype=eventtype, **validated_data)
event.save()
return event
Error when owner field present:
False
{'owner': OrderedDict([('username', ['A user with that username already exists.'])])}
Result when UserSerializer(read_only=True) (pretty much diabling it):
True
OrderedDict([('eventtype', OrderedDict([('lname', 'Swimming'), ('sname', 'SWM'), ('category', '1')])), ('date', datetime.date(2015, 12, 22)), ('$
(Notice the event type data in the result)
Thanks!
You need to remove the validators from UserSerializer.
Assuming UserSerializer is a User ModelSerializer it'll extract the unique constraint on the User.username from the Model and your validation will fail.
To work this around you'll need to remove the UniqueValidator by overriding the validators list for the username field of the UserSerializer