I have a model with a unique field like so:
models.py
class MyModel(...):
name = models.CharField(max_length=32, ...)
key = models.CharField(max_length=32, ...)
class Meta:
constraints = [
UniqueConstraint(
fields = ['name', 'key'],
...
),
]
If I send a POST request where name is more than 32 characters, I get back a proper error response:
{"name": ["Ensure this field has no more than 32 characters."]}
However, if I send a POST request where the combination of name and key is not unique, an exception is raised and no message is sent back to the client. How can I capture when a unique constraint is violated and send back a message to the client?
Found the solution, DRF provides UniqueValidators link:
from rest_framework.validators import UniqueTogetherValidator
class ExampleSerializer(serializers.Serializer):
# ...
class Meta:
# ToDo items belong to a parent list, and have an ordering defined
# by the 'position' field. No two items in a given list may share
# the same position.
validators = [
UniqueTogetherValidator(
queryset=ToDoItem.objects.all(),
fields=['list', 'position']
)
]
Related
I'm creating a search functionality that returns all model fields like - users, posts, hashtags, images, and videos.
I'm trying to create multiple queryset with their multiple serializers to return in a single response.
this is my expected response result.
{
searchresults{
posts[
],images[
],videos[
],user[
]
}
}
I tried many methods to do this, but the response was unsuccessful.
I also tried that one but that required relationship.
class HashTagResponseSerializer(serializers.ModelSerializer):
class Meta:
model = HashtagName
fields = ["hashtag",]
def get_hashtag(self, obj):
...
class UserFollowSerializer(serializers.ModelSerializer):
hashtag = HashTagResponseSerializer(many=True, read_only=True)
class Meta:
model = User
fields = ['user_uuid', 'post_language', "username", "display_name",
"profile_picture", "hashtag "]
I have a Django app, and need to send email notifications to certain users at certain points in my processes without adding any external libraries. For example, if any user submits a particular form, I need to send a notification to a specific set of users.
I'm thinking to use custom model permissions for each model that relates to a particular form. Then hypothetically add users to unique Authentication Groups that have those particular permissions.
Then I would create a Notifier class with a notify_users_by_email method that could accept an authorization group and a reason type. Have each reason type be 1-to-1 with the permissions defined at the model level. Then we can just filter the group to the users in the group and send the email.
I would also add a Notifications model, which could be used to record who sent the notifications, who was the intended recipient, send time and the notification reason (again related to permission).
In general, is that an appropriate way of going about this, or is there a cleaner way to do this in Django?
class Notifier:
#classmethod
def notify_group_by_email(cls, group_id: int, reason: str):
users_in_group = Group.objects.get(id=group_id).user_set.all()
for user in users_in_group:
cls.notify_user_by_email(user_id=user.id, reason=reason)
user = User.objects.get(id=user.id)
email_string = f"{user.username}#gmail.com"
print(f"Notified user {email_string} re: {reason}")
#classmethod
def notify_user_by_email(cls, reason: str, user_id: int):
assert isinstance(reason, str)
assert isinstance(user_id, int)
Notification class:
class Notification(DateUserFieldsAbstract):
id = models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID')
sender_id = models.ForeignKey('User', on_delete=models.PROTECT)
receiver_id = models.ForeignKey('User', on_delete=models.PROTECT)
reason = models.CharField(max_length=100)
type = models.CharField(max_length=100)
I'm using Django 3 and Python 3.8. I have the below model, Note the "types" ManyToMany field, in which I set "blank" to False.
class Coop(models.Model):
objects = CoopManager()
name = models.CharField(max_length=250, null=False)
types = models.ManyToManyField(CoopType, blank=False)
addresses = models.ManyToManyField(Address)
enabled = models.BooleanField(default=True, null=False)
phone = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_phone')
email = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_email')
web_site = models.TextField()
I want verify a validation error occurs if I leave that field blank, so I have
#pytest.mark.django_db
def test_coop_create_with_no_types(self):
""" Verify can't create coop if no """
coop = CoopFactory.create(types=[])
self.assertIsNotNone(coop)
self.assertNone( coop.id )
and use the following factory (with FactoryBoy) to build the model
class CoopFactory(factory.DjangoModelFactory):
"""
Define Coop Factory
"""
class Meta:
model = Coop
name = "test model"
enabled = True
phone = factory.SubFactory(PhoneContactMethodFactory)
email = factory.SubFactory(EmailContactMethodFactory)
web_site = "http://www.hello.com"
#factory.post_generation
def addresses(self, create, extracted, **kwargs):
if not create:
# Simple build, do nothing.
return
if extracted:
# A list of types were passed in, use them
for address in extracted:
self.addresses.add(address)
else:
address = AddressFactory()
self.addresses.add( address )
#factory.post_generation
def types(self, create, extracted, **kwargs):
if not create:
# Simple build, do nothing.
return
if extracted:
# A list of types were passed in, use them
for _ in range(extracted):
self.types.add(CoopTypeFactory())
However, the "self.assertNone( coop.id )" assertion fails (an ID is generated). I would expect this not to happen, since I haven't specified any types. What else do I need to do to enforce my constraint, or should I be using a different constraint?
Edit: In response to #Melvyn's suggestion, tried modifying the test to the below
#pytest.mark.django_db
def test_coop_create_with_no_types(self):
""" Test customer model """ # create customer model instance
coop = CoopFactory.build(types=[])
coop.full_clean()
self.assertIsNotNone(coop)
self.assertIsNone( coop.id )
but not only did not get a validation error for the "types," field, got validation errors for the email and phone fields, which are clearly being populated in the factory.
File "/Users/davea/Documents/workspace/chicommons/maps/web/tests/test_models.py", line 76, in test_coop_create_with_no_types
coop.full_clean()
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/django/db/models/base.py", line 1221, in full_clean
raise ValidationError(errors)
django.core.exceptions.ValidationError: {'phone': ['This field cannot be blank.'], 'email': ['This field cannot be blank.']}
Edit: Per the answer given #ArakkalAbu, I implemented the suggestion (https://github.com/chicommons/maps/blob/master/web/directory/serializers.py) but this test continues to pass
#pytest.mark.django_db
def test_coop_create_no_coop_types(self):
""" Test coop serizlizer model """
name = "Test 8899"
street = "222 W. Merchandise Mart Plaza, Suite 1212"
city = "Chicago"
postal_code = "60654"
enabled = True
postal_code = "60654"
email = "test#example.com"
phone = "7732441468"
web_site = "http://www.1871.com"
state = StateFactory()
serializer_data = {
"name": name,
"types": [
],
"addresses": [{
"formatted": street,
"locality": {
"name": city,
"postal_code": postal_code,
"state": state.id
}
}],
"enabled": enabled,
"phone": {
"phone": phone
},
"email": {
"email": email
},
"web_site": web_site
}
serializer = CoopSerializer(data=serializer_data)
assert serializer.is_valid(True), serializer.errors
You can't enforce this constrain, blank=False into neither database-level nor model-level. Because every m2m relation has a record with a foreign key to both sides of the m2m relation (the through--(Django Doc) relation).
Also in m2m relations, the m2m items are linked by a separate operation under the hood.
Create CoopFactory instance
Add CoopType to Coop.types by using .add()--(django doc) or .set()--(django doc) methods
That is, you can not create a M2M relation directly by
Coop.objects.create(name='foo', types=[1, 2, 3]) # 1,2 & 3 are PKs of `CoopType`
This statement trigger an exception by saying,
TypeError: Direct assignment to the forward side of a many-to-many set is prohibited. Use types.set() instead.
What is the best bet?
as per your this comment,
I'm not using this in a form though. I'm using the model by a serializer as part of the Django rest framework.
Since you are using DRF, you can validate the incoming payload.
class CoopSerializer(serializers.ModelSerializer):
class Meta:
model = Coop
fields = '__all__'
extra_kwargs = {
'types': {
'allow_empty': False
}
}
# execution
s = CoopSerializer(data={'name': 'foo coop', 'types': []})
s.is_valid(True)
s.save()
# excption
rest_framework.exceptions.ValidationError: {'types': [ErrorDetail(string='This list may not be empty.', code='empty')]}
This will help you to enforce to have a required M2M data.
blank=True on a ManyToManyField is not translated as a DBMS constraint, but will be (for exemple) checked on form validation.
On your unit-test, you use CoopFactory.create that seems to not check this logical (and non-dbms) constraint.
See https://docs.djangoproject.com/en/3.0/ref/models/fields/#blank
Note that this is different than null. null is purely database-related, whereas blank is validation-related. If a field has blank=True, form validation will allow entry of an empty value. If a field has blank=False, the field will be required.
I'm trying to remove key pair values if they are not provided in my payload from SQLAlchemy query.
payload1 = {
"username": "James",
"ssn": "102-00-1911",
"state": "New York"
}
payload2 = {
"username": "James"
}
def search_database(body):
"""
Searches Database for user
"""
query = db_session.query(DatabaseModel).filter_by(
username=body['username'],
ssn=body.get('ssn', None), # kwarg should be omitted if it's not in body
state=body.get('state', None), # kwarg should be omitted if it's not in body
).all()
return query
search_database(payload1) # should not omit anything
search_database(payload2) # Should omit `ssn` and `state` from query
Currently I'm unable to find a way to dynamically remove a key pair from my query. I tried passing None as an alternative to the value if a key is missing but that did not work.
If the payload is missing ssn or state I would like to omit it from the query
I realized you can pass a dictionary in filter_by. I was able to filter using a Dict comprehension on the body before passing it to the query.
def search_database(body):
"""
Searches Database for user
"""
permitted = [
"username",
"ssn",
"state"
]
filtered = {key: body[key] for key in permitted if key in body}
query = db_session.query(DatabaseModel).filter_by(**filtered).all()
return query
I use wp job manager on my website. when I tried to add listing by xmlrpc, everything is fine, but Categories and Location are empty.
Screenshot
Screenshot
My code is as below. Could you tell me what's wrong with my code?
Thanks
from wordpress_xmlrpc import Client, WordPressPost
from wordpress_xmlrpc.methods.posts import GetPosts
from wordpress_xmlrpc.methods import posts
from wordpress_xmlrpc import WordPressTerm
from wordpress_xmlrpc.methods import taxonomies
wp = Client('http://127.0.0.1/15wp/xmlrpc.php', 'admin', '123456')
# now let's create a new product
widget = WordPressPost()
widget.post_type = 'job_listing'
widget.title = 'Widgetlast02'
widget.content = 'This is the widgets description.'
widget.post_status = 'publish'
widget.custom_fields = []
widget.custom_fields.append({
'job_location': 'Newyork',
'job_listing_category': 'hotel'
})
widget.id = wp.call(posts.NewPost(widget))
The custom_fields attribute expects a list of dicts.
Within each dict, it expects values for key and value fields.
Here, key is the name of the custom field, and value is the value you want to assign to it.
Below is the snippet for your specific example.
widget.custom_fields = [
{
'key': 'job_location',
'value': 'Newyork'
},
{
'key': 'job_listing_category',
'value': 'hotel'
}
]
This is just a guess from looking at the documentation for WordPressPost in wordpress_xmlrpc:
(Emphasis mine)
class WordPressPost
Represents a post, page, or other registered custom post type in
WordPress.
id
user
date (datetime)
date_modified (datetime)
slug
post_status
title
content
excerpt
link
comment_status
ping_status
terms (list of WordPressTerms)
terms_names (dict)
custom_fields (dict)
enclosure (dict)
password
post_format
thumbnail
sticky
post_type
It expects custom_fields to be a dict - you're creating a list and then inserting a dict into that list:
widget.custom_fields = []
widget.custom_fields.append({
'job_location': 'Newyork',
'job_listing_category': 'hotel'
})
This will probably work better:
widget.custom_fields = {
'job_location': 'Newyork',
'job_listing_category': 'hotel'
}